Skip to content

Commit

Permalink
Merge pull request #499 from LedgerHQ/cev/B2CA-969_ccid-pinpad
Browse files Browse the repository at this point in the history
B2CA-969: Activate pinpad and fix CCID SC_Secure
  • Loading branch information
cedelavergne-ledger authored Jan 18, 2024
2 parents bb1971c + 1bdd759 commit 3957522
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 59 deletions.
5 changes: 2 additions & 3 deletions lib_stusb/STM32_USB_Device_Library/Class/CCID/inc/sc_itf.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,15 @@ uint8_t SC_ExecuteEscape (uint8_t* escapePtr, uint32_t escapeLen,
uint8_t* responseBuff,
uint16_t* responseLen);
uint8_t SC_SetClock (uint8_t bClockCommand);
uint8_t SC_XferBlock (uint8_t* ptrBlock, uint32_t blockLen, uint16_t* expectedLen);
uint8_t SC_XferBlock (uint8_t* ptrBlock, uint32_t blockLen);
uint8_t SC_Request_GetClockFrequencies(uint8_t* pbuf, uint16_t* len);
uint8_t SC_Request_GetDataRates(uint8_t* pbuf, uint16_t* len);
uint8_t SC_T0Apdu(uint8_t bmChanges, uint8_t bClassGetResponse,
uint8_t bClassEnvelope);
uint8_t SC_Mechanical(uint8_t bFunction);
uint8_t SC_SetDataRateAndClockFrequency(uint32_t dwClockFrequency,
uint32_t dwDataRate);
uint8_t SC_Secure(uint32_t dwLength, uint8_t bBWI, uint16_t wLevelParameter,
uint8_t* pbuf, uint32_t* returnLen );
uint8_t SC_Secure(uint8_t* pbuf, uint32_t* returnLen);

#endif // HAVE_USB_CLASS_CCID

Expand Down
10 changes: 10 additions & 0 deletions lib_stusb/STM32_USB_Device_Library/Class/CCID/inc/usbd_ccid_if.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@
#define VOLTAGE_SELECTION_5V 0x01
#define VOLTAGE_SELECTION_1V8 0x03

/* PC_to_RDR_Secure PIN operations */
#define PIN_OPR_VERIFICATION 0x00
#define PIN_OPR_MODIFICATION 0x01
#define PIN_OPR_TRANSFER 0x02
#define PIN_OPR_WAIT_ICC_RESP 0x03
#define PIN_OPR_CANCEL 0x04
#define PIN_OPR_APDU_CLA 0xEF

#define PC_TO_RDR_ICCPOWERON 0x62
#define PC_TO_RDR_ICCPOWEROFF 0x63
#define PC_TO_RDR_GETSLOTSTATUS 0x65
Expand Down Expand Up @@ -223,6 +231,8 @@ void Set_CSW (uint8_t CSW_Status, uint8_t Send_Permission);

void io_usb_ccid_set_card_inserted(unsigned int inserted);

void io_usb_ccid_configure_pinpad(uint8_t enabled);

#endif // HAVE_USB_CLASS_CCID

#endif /* __USBD_CCID_IF_H */
Expand Down
35 changes: 14 additions & 21 deletions lib_stusb/STM32_USB_Device_Library/Class/CCID/src/usbd_ccid_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,28 +231,25 @@ uint8_t PC_to_RDR_XfrBlock(void)
if (error != 0)
return error;

if (G_io_ccid.bulk_header.bulkout.dwLength > IO_CCID_DATA_BUFFER_SIZE)
{ /* Check amount of Data Sent by Host is > than memory allocated ? */
if (G_io_ccid.bulk_header.bulkout.dwLength > IO_CCID_DATA_BUFFER_SIZE)
{ /* Check amount of Data Sent by Host is > than memory allocated ? */

return SLOTERROR_BAD_DWLENGTH;
}
return SLOTERROR_BAD_DWLENGTH;
}


/* wLevelParameter = Size of expected data to be returned by the
bulk-IN endpoint */
expectedLength = (G_io_ccid.bulk_header.bulkout.bSpecific_2 << 8) |
G_io_ccid.bulk_header.bulkout.bSpecific_1;
/* wLevelParameter = Size of expected data to be returned by the
bulk-IN endpoint */
expectedLength = (G_io_ccid.bulk_header.bulkout.bSpecific_2 << 8) |
G_io_ccid.bulk_header.bulkout.bSpecific_1;

reqlen = G_io_ccid.bulk_header.bulkout.dwLength;

G_io_ccid.bulk_header.bulkin.dwLength = (uint16_t)expectedLength;
reqlen = G_io_ccid.bulk_header.bulkout.dwLength;

G_io_ccid.bulk_header.bulkin.dwLength = (uint16_t)expectedLength;

error = SC_XferBlock(&G_io_ccid_data_buffer[0],
reqlen,
&expectedLength);
error = SC_XferBlock(&G_io_ccid_data_buffer[0], reqlen);

if (error != SLOT_NO_ERROR)
if (error != SLOT_NO_ERROR)
{
CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE);
}
Expand Down Expand Up @@ -639,7 +636,6 @@ uint8_t PC_TO_RDR_SetDataRateAndClockFrequency(void)
uint8_t error;
uint32_t clockFrequency;
uint32_t dataRate;
uint32_t temp =0;

error = CCID_CheckCommandParams(CHK_PARAM_SLOT |\
CHK_PARAM_CARD_PRESENT |\
Expand Down Expand Up @@ -694,7 +690,6 @@ uint8_t PC_TO_RDR_SetDataRateAndClockFrequency(void)
uint8_t PC_TO_RDR_Secure(void)
{
uint8_t error;
uint8_t bBWI;
uint16_t wLevelParameter;
uint32_t responseLen;

Expand All @@ -708,14 +703,13 @@ uint8_t PC_TO_RDR_Secure(void)
return error;
}

bBWI = G_io_ccid.bulk_header.bulkout.bSpecific_0;
wLevelParameter = (G_io_ccid.bulk_header.bulkout.bSpecific_1 + ((uint16_t)G_io_ccid.bulk_header.bulkout.bSpecific_2<<8));

if ((EXCHANGE_LEVEL_FEATURE == TPDU_EXCHANGE) ||
(EXCHANGE_LEVEL_FEATURE == SHORT_APDU_EXCHANGE))
{
/* TPDU level & short APDU level, wLevelParameter is RFU, = 0000h */
if (wLevelParameter != 0 )
if (wLevelParameter != 0)
{
G_io_ccid.bulk_header.bulkin.dwLength = 0;
CCID_UpdateCommandStatus(BM_COMMAND_STATUS_FAILED, BM_ICC_PRESENT_ACTIVE);
Expand All @@ -724,8 +718,7 @@ uint8_t PC_TO_RDR_Secure(void)
}
}

error = SC_Secure(G_io_ccid.bulk_header.bulkout.dwLength - CCID_HEADER_SIZE, bBWI, wLevelParameter,
&G_io_ccid_data_buffer[0], &responseLen);
error = SC_Secure(&G_io_ccid_data_buffer[0], &responseLen);

G_io_ccid.bulk_header.bulkin.dwLength = responseLen;

Expand Down
66 changes: 36 additions & 30 deletions lib_stusb/STM32_USB_Device_Library/Class/CCID/src/usbd_ccid_if.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ void CCID_BulkMessage_Out (USBD_HandleTypeDef *pdev,
G_io_ccid.Ccid_BulkState = CCID_STATE_IDLE;
// no break is intentional

__attribute__((fallthrough));
case CCID_STATE_IDLE:
// prepare to receive another packet later on (to avoid troubles with timeout due to other hid command timeouting the ccid endpoint reply)
USBD_LL_PrepareReceive(pdev, CCID_BULK_OUT_EP, CCID_BULK_EPOUT_SIZE);
Expand Down Expand Up @@ -525,49 +526,55 @@ uint8_t SC_SetDataRateAndClockFrequency(uint32_t dwClockFrequency,
UNUSED(dwDataRate);
return SLOT_NO_ERROR;
}
uint8_t SC_Secure(uint32_t dwLength, uint8_t bBWI, uint16_t wLevelParameter,
uint8_t* pbuf, uint32_t* returnLen ) {
UNUSED(bBWI);
UNUSED(wLevelParameter);
UNUSED(returnLen);
// return SLOTERROR_CMD_NOT_SUPPORTED;
uint16_t ret_len,off;
uint8_t SC_Secure(uint8_t* pbuf, uint32_t* returnLen) {
// Extract the APDU to send to the App
switch(pbuf[0]) {
case 0: // verify pin
ret_len = dwLength - 15;
memmove(G_io_apdu_buffer, pbuf+15, dwLength-15);
case PIN_OPR_VERIFICATION:
// CCID Spec: APDU starts at offset 25, after the 10-Byte header
pbuf += 15;
break;
case 1: // modify pin
case PIN_OPR_MODIFICATION:
// CCID Spec: APDU starts at offset 28, 29 or 30
// depending on the nb of messages to display
switch(pbuf[11]) {
case 3:
off = 20;
case 0:
// CCID Spec: No message to display
// APDU starts at offset 28, after the 10-Byte header
pbuf += 18;
break;
case 2:
case 1:
off = 19;
case 2:
// CCID Spec: 1 or 2 message(s) to display
// APDU starts at offset 29, after the 10-Byte header
pbuf += 19;
break;
// 0 and 4-0xFF
default:
off = 18;
case 3:
// CCID Spec: 3 messages to display
// APDU starts at offset 30, after the 10-Byte header
pbuf += 20;
break;
default: // unsupported
G_io_ccid.bulk_header.bulkin.dwLength = 0;
RDR_to_PC_DataBlock(SLOTERROR_CMD_NOT_SUPPORTED);
CCID_Send_Reply(&USBD_Device);
return SLOTERROR_CMD_NOT_SUPPORTED;
}
ret_len = dwLength-off;
// provide with the complete apdu
memmove(G_io_apdu_buffer, pbuf+off, dwLength-off);
break;
default: // unsupported
G_io_ccid.bulk_header.bulkin.dwLength = 0;
RDR_to_PC_DataBlock(SLOTERROR_CMD_NOT_SUPPORTED);
CCID_Send_Reply(&USBD_Device);
return SLOTERROR_CMD_NOT_SUPPORTED;
}
return SC_XferBlock(G_io_apdu_buffer, ret_len, &ret_len);
// Change APDU CLA to be interpreted by the CCID compatible App (like OpenPGP)
pbuf[0] = PIN_OPR_APDU_CLA;
// The APDU has no data, only the header (size 5)
*returnLen = 5;
return SC_XferBlock(pbuf, *returnLen);
}

// prepare the apdu to be processed by the application
uint8_t SC_XferBlock (uint8_t* ptrBlock, uint32_t blockLen, uint16_t* expectedLen) {
UNUSED(expectedLen);

uint8_t SC_XferBlock (uint8_t* ptrBlock, uint32_t blockLen) {
// check for overflow
if (blockLen > IO_APDU_BUFFER_SIZE) {
return SLOTERROR_BAD_LENTGH;
Expand Down Expand Up @@ -614,11 +621,10 @@ void io_usb_ccid_set_card_inserted(unsigned int inserted) {
#endif // HAVE_CCID_INTERRUPT
}






void io_usb_ccid_configure_pinpad(uint8_t enabled) {
const volatile uint8_t *cfgDesc = USBD_GetPinPadOffset();
nvm_write((void *)cfgDesc, &enabled, 1);
}

#endif // HAVE_USB_CLASS_CCID

Expand Down
1 change: 1 addition & 0 deletions lib_stusb/usbd_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ typedef unsigned short uint16_t;
void *USBD_static_malloc(uint32_t size);
void USBD_static_free(void *p);

const volatile uint8_t *USBD_GetPinPadOffset(void);

void USB_power(unsigned char enabled);

Expand Down
56 changes: 51 additions & 5 deletions lib_stusb_impl/usbd_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,21 +353,34 @@ static uint8_t const HID_ReportDesc_fido[] = {

#define ARRAY_U2LE(l) (l)&0xFF, (l)>>8

#define CFG_HDR_LEN (0x9)
#define CFG_HIDGEN_LEN (0x9+0x9+0x7+0x7)
#define CFG_IO_U2F_LEN (0x9+0x9+0x7+0x7)
#define CFG_USB_CCID_LEN (0x9+0x36+0x7+0x7)
#define CFG_WEBUSB_LEN (0x9+0x7+0x7)

/* USB HID device Configuration Descriptor */
#ifdef HAVE_USB_CLASS_CCID
// Note: keeping const qualifier to ensure the good section is used by the linker.
// This table is mapped in NVRAM section and therefore it is correctly initialized,
// normal Read is possible and Write can be done through nvm_write() calls.
static __ALIGN_BEGIN uint8_t const N_USBD_CfgDesc[] __ALIGN_END =
#else
static __ALIGN_BEGIN uint8_t const USBD_CfgDesc[] __ALIGN_END =
#endif // HAVE_USB_CLASS_CCID
{
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
ARRAY_U2LE(0x9 /* wTotalLength: Bytes returned */
+0x9+0x9+0x7+0x7
ARRAY_U2LE(CFG_HDR_LEN /* wTotalLength: Bytes returned */
+CFG_HIDGEN_LEN
#ifdef HAVE_IO_U2F
+0x9+0x9+0x7+0x7
+CFG_IO_U2F_LEN
#endif // HAVE_IO_U2F
#ifdef HAVE_USB_CLASS_CCID
+0x9+0x36+0x7+0x7
+CFG_USB_CCID_LEN
#endif // HAVE_USB_CLASS_CCID
#ifdef HAVE_WEBUSB
+0x9+0x7+0x7
+CFG_WEBUSB_LEN
#endif // HAVE_WEBUSB
),
1
Expand Down Expand Up @@ -870,8 +883,13 @@ static uint8_t *USBD_GetDeviceQualifierDesc_impl(uint16_t *length)
*/
static uint8_t *USBD_GetCfgDesc_impl(uint16_t *length)
{
#ifdef HAVE_USB_CLASS_CCID
*length = sizeof(N_USBD_CfgDesc);
return (uint8_t *) N_USBD_CfgDesc;
#else
*length = sizeof(USBD_CfgDesc);
return (uint8_t *) USBD_CfgDesc;
#endif
}

uint8_t *USBD_HID_GetHidDescriptor_impl(uint16_t *len)
Expand Down Expand Up @@ -927,6 +945,34 @@ uint8_t *USBD_HID_GetReportDescriptor_impl(uint16_t *len)
return 0;
}

#ifdef HAVE_USB_CLASS_CCID
/**
* @brief Returns the pinpad value offset in the descriptor.
* @retval Offset
*/
const volatile uint8_t *USBD_GetPinPadOffset(void)
{
unsigned short length = 0;
uint8_t *cfgDesc = NULL;
unsigned short offset = 0;

cfgDesc = USBD_GetCfgDesc_impl(&length);

offset = CFG_HDR_LEN + CFG_HIDGEN_LEN;
#ifdef HAVE_IO_U2F
offset += CFG_IO_U2F_LEN;
#endif // HAVE_IO_U2F
// Offset of the parameter 'bPINSupport' inside the CCID interface structure in N_USBD_CfgDesc
offset += 61;

// Returns a const volatile pointer allowing callers to do
// - write operations through nvram_write
// - read operation without potential compilation optimization issue thanks to the volatile
// qualifier.
return (const volatile uint8_t *) (cfgDesc + offset);
}
#endif // HAVE_USB_CLASS_CCID

/**
* @}
*/
Expand Down

0 comments on commit 3957522

Please sign in to comment.