It's a major reorganization.

In order to add FIDO2 support, we need to reorganize some USB/CCID calls to specific area (named driver).
Thus, pico-hsm-sdk has two drivers:
- CCID driver implements APDU over USB/CCID ISO-7816 standard procedures.
- HID driver implements APDU over HID.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos
2022-08-30 02:00:11 +02:00
parent 2236501d20
commit 40288a85f1
33 changed files with 1843 additions and 289 deletions

350
src/usb/ccid/ccid.c Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
// Pico
#include "pico/stdlib.h"
// For memcpy
#include <string.h>
// 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;
}

49
src/usb/ccid/ccid.h Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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_

119
src/usb/ccid/tusb_config.h Normal file
View File

@@ -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_ */

View File

@@ -0,0 +1,325 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _USB_COMMON_H
#define _USB_COMMON_H
#include "pico/types.h"
#include "hardware/structs/usb.h"
// bmRequestType bit definitions
#define USB_REQ_TYPE_STANDARD 0x00u
#define USB_REQ_TYPE_TYPE_MASK 0x60u
#define USB_REQ_TYPE_TYPE_CLASS 0x20u
#define USB_REQ_TYPE_TYPE_VENDOR 0x40u
#define USB_REQ_TYPE_RECIPIENT_MASK 0x1fu
#define USB_REQ_TYPE_RECIPIENT_DEVICE 0x00u
#define USB_REQ_TYPE_RECIPIENT_INTERFACE 0x01u
#define USB_REQ_TYPE_RECIPIENT_ENDPOINT 0x02u
#define USB_DIR_OUT 0x00u
#define USB_DIR_IN 0x80u
#define USB_TRANSFER_TYPE_CONTROL 0x0
#define USB_TRANSFER_TYPE_ISOCHRONOUS 0x1
#define USB_TRANSFER_TYPE_BULK 0x2
#define USB_TRANSFER_TYPE_INTERRUPT 0x3
#define USB_TRANSFER_TYPE_BITS 0x3
// Descriptor types
#define USB_DT_DEVICE 0x01
#define USB_DT_CONFIG 0x02
#define USB_DT_STRING 0x03
#define USB_DT_INTERFACE 0x04
#define USB_DT_ENDPOINT 0x05
#define USB_REQUEST_GET_STATUS 0x0
#define USB_REQUEST_CLEAR_FEATURE 0x01
#define USB_REQUEST_SET_FEATURE 0x03
#define USB_REQUEST_SET_ADDRESS 0x05
#define USB_REQUEST_GET_DESCRIPTOR 0x06
#define USB_REQUEST_SET_DESCRIPTOR 0x07
#define USB_REQUEST_GET_CONFIGURATION 0x08
#define USB_REQUEST_SET_CONFIGURATION 0x09
#define USB_REQUEST_GET_INTERFACE 0x0a
#define USB_REQUEST_SET_INTERFACE 0x0b
#define USB_REQUEST_SYNC_FRAME 0x0c
#define USB_REQUEST_MSC_GET_MAX_LUN 0xfe
#define USB_REQUEST_MSC_RESET 0xff
#define USB_FEAT_ENDPOINT_HALT 0x00
#define USB_FEAT_DEVICE_REMOTE_WAKEUP 0x01
#define USB_FEAT_TEST_MODE 0x02
#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05
struct usb_setup_packet {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} __packed;
struct usb_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
};
struct usb_device_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
} __packed;
struct usb_configuration_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
} __packed;
struct usb_interface_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
} __packed;
struct usb_endpoint_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
} __packed;
struct usb_endpoint_descriptor_long {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
uint8_t bRefresh;
uint8_t bSyncAddr;
} __attribute__((packed));
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 ccid_desc = {
.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,
};
// Struct in which we keep the endpoint configuration
typedef void (*usb_ep_handler)(uint8_t *buf, uint16_t len);
struct usb_endpoint_configuration {
const struct usb_endpoint_descriptor *descriptor;
usb_ep_handler handler;
// Pointers to endpoint + buffer control registers
// in the USB controller DPSRAM
volatile uint32_t *endpoint_control;
volatile uint32_t *buffer_control;
volatile uint8_t *data_buffer;
// Toggle after each packet (unless replying to a SETUP)
uint8_t next_pid;
};
// Struct in which we keep the device configuration
struct usb_device_configuration {
const struct usb_device_descriptor *device_descriptor;
const struct usb_interface_descriptor *interface_descriptor;
const struct usb_configuration_descriptor *config_descriptor;
const struct ccid_class_descriptor *ccid_descriptor;
const unsigned char *lang_descriptor;
const unsigned char **descriptor_strings;
// USB num endpoints is 16
struct usb_endpoint_configuration endpoints[USB_NUM_ENDPOINTS];
};
#define EP0_IN_ADDR (USB_DIR_IN | 0)
#define EP0_OUT_ADDR (USB_DIR_OUT | 0)
#define EP1_OUT_ADDR (USB_DIR_OUT | 1)
#define EP2_IN_ADDR (USB_DIR_IN | 2)
// EP0 IN and OUT
static const struct usb_endpoint_descriptor ep0_out = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP0_OUT_ADDR, // EP number 0, OUT from host (rx to device)
.bmAttributes = USB_TRANSFER_TYPE_CONTROL,
.wMaxPacketSize = 64,
.bInterval = 0
};
static const struct usb_endpoint_descriptor ep0_in = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP0_IN_ADDR, // EP number 0, OUT from host (rx to device)
.bmAttributes = USB_TRANSFER_TYPE_CONTROL,
.wMaxPacketSize = 64,
.bInterval = 0
};
// Descriptors
static const struct usb_device_descriptor device_descriptor = {
.bLength = sizeof(struct usb_device_descriptor),
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200, // USB 1.1 device
.bDeviceClass = 0, // Specified in interface descriptor
.bDeviceSubClass = 0, // No subclass
.bDeviceProtocol = 0, // No protocol
.bMaxPacketSize0 = 64, // Max packet size for ep0
.idVendor = 0x20a0, // Your vendor id
.idProduct = 0x4230, // Your product ID
.bcdDevice = 0x0101, // No device revision number
.iManufacturer = 1, // Manufacturer string index
.iProduct = 2, // Product string index
.iSerialNumber = 3, // No serial number
.bNumConfigurations = 1 // One configuration
};
static const struct usb_interface_descriptor interface_descriptor = {
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2, // Interface has 2 endpoints
.bInterfaceClass = 0x0b, // Vendor specific endpoint
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 5
};
static const struct usb_endpoint_descriptor ep1_out = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP1_OUT_ADDR, // EP number 1, OUT from host (rx to device)
.bmAttributes = USB_TRANSFER_TYPE_BULK,
.wMaxPacketSize = 64,
.bInterval = 0
};
static const struct usb_endpoint_descriptor ep2_in = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = EP2_IN_ADDR, // EP number 2, IN from host (tx from device)
.bmAttributes = USB_TRANSFER_TYPE_BULK,
.wMaxPacketSize = 64,
.bInterval = 0
};
static const struct usb_configuration_descriptor config_descriptor = {
.bLength = sizeof(struct usb_configuration_descriptor),
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = (sizeof(config_descriptor) +
sizeof(interface_descriptor) +
sizeof(ccid_desc) +
sizeof(ep1_out) +
sizeof(ep2_in)),
.bNumInterfaces = 1,
.bConfigurationValue = 1, // Configuration 1
.iConfiguration = 4, // No string
.bmAttributes = 0xa0, // attributes: self powered, no remote wakeup
.bMaxPower = 0x32 // 100ma
};
static const unsigned char lang_descriptor[] = {
4, // bLength
0x03, // bDescriptorType == String Descriptor
0x09, 0x04 // language id = us english
};
#define USB_REQ_CCID 0xA1
extern uint16_t usb_read(uint8_t *buffer, size_t buffer_size);
extern uint16_t usb_read_available();
extern uint32_t usb_write_offset(uint16_t size, uint16_t offset);
extern uint32_t usb_write(uint16_t size);
extern bool usb_is_configured();
extern void usb_init();
extern uint8_t *usb_get_rx();
extern uint32_t usb_send_tx_buffer();
extern uint8_t *usb_get_tx();
extern void usb_clear_rx();
extern bool usb_write_available();
extern uint32_t usb_write_flush();
#endif

View File

@@ -0,0 +1,233 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "tusb.h"
#include "usb_descriptors.h"
#include "pico/unique_id.h"
#include "hsm_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
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,
};
//--------------------------------------------------------------------+
// 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_SDK_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<chr_count; i++) {
_desc_str[1+i] = str[i];
}
}
_desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
return _desc_str;
}

View File

@@ -0,0 +1,46 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef USB_DESCRIPTORS_H_
#define USB_DESCRIPTORS_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__));
#endif /* USB_DESCRIPTORS_H_ */