From 21debc056763d766249626d704e1f9ba7e8d0b48 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Fri, 20 Jan 2012 18:18:23 +0900 Subject: [PATCH] no ext lc and le but short APDU only --- STM32_USB-FS-Device_Driver/src/usb_core.c | 7 +- src/call-rsa.c | 2 +- src/gnuk.h | 21 +- src/main.c | 7 + src/openpgp-do.c | 24 +- src/openpgp.c | 5 +- src/openpgp.h | 24 +- src/stmusb.mk | 2 +- src/usb-icc.c | 1476 +++++++++++++-------- src/usb_desc.c | 4 +- src/usb_lld.c | 73 + src/usb_lld.h | 13 +- tool/gnuk_put_binary.py | 32 +- 13 files changed, 1064 insertions(+), 626 deletions(-) diff --git a/STM32_USB-FS-Device_Driver/src/usb_core.c b/STM32_USB-FS-Device_Driver/src/usb_core.c index 8220ef3..24ee765 100644 --- a/STM32_USB-FS-Device_Driver/src/usb_core.c +++ b/STM32_USB-FS-Device_Driver/src/usb_core.c @@ -15,6 +15,9 @@ /* Includes ------------------------------------------------------------------*/ #include "usb_lib.h" +extern void usb_lld_to_pmabuf (const void *src, uint16_t addr, uint32_t n); +extern void usb_lld_from_pmabuf (void *dst, uint16_t addr, uint32_t n); + /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ #define ValBit(VAR,Place) (VAR & (1 << Place)) @@ -460,7 +463,7 @@ void DataStageOut(void) #ifdef STM32F10X_CL OTGD_FS_PCD_EP_Read(ENDP0, Buffer, Length); #else - PMAToUserBufferCopy(Buffer, GetEPRxAddr(ENDP0), Length); + usb_lld_from_pmabuf (Buffer, GetEPRxAddr(ENDP0), Length); #endif /* STM32F10X_CL */ } @@ -544,7 +547,7 @@ void DataStageIn(void) #ifdef STM32F10X_CL OTGD_FS_PCD_EP_Write (ENDP0, DataBuffer, Length); #else - UserToPMABufferCopy(DataBuffer, GetEPTxAddr(ENDP0), Length); + usb_lld_to_pmabuf (DataBuffer, GetEPTxAddr(ENDP0), Length); #endif /* STM32F10X_CL */ SetEPTxCount(ENDP0, Length); diff --git a/src/call-rsa.c b/src/call-rsa.c index cf7530b..f9a90ef 100644 --- a/src/call-rsa.c +++ b/src/call-rsa.c @@ -167,7 +167,7 @@ rsa_decrypt (const uint8_t *input, uint8_t *output, int msg_len, #endif r = rsa_pkcs1_decrypt (&rsa_ctx, RSA_PRIVATE, &output_len, - input, output, MAX_RES_APDU_SIZE - 2); + input, output, MAX_RES_APDU_DATA_SIZE); rsa_free (&rsa_ctx); if (r < 0) { diff --git a/src/gnuk.h b/src/gnuk.h index 464e036..f5a2f43 100644 --- a/src/gnuk.h +++ b/src/gnuk.h @@ -36,7 +36,7 @@ struct apdu { uint8_t *cmd_apdu_head; /* CLS INS P1 P2 [ internal Lc ] */ uint8_t *cmd_apdu_data; uint16_t cmd_apdu_data_len; /* Nc, calculated by Lc field */ - uint32_t expected_res_size; /* Ne, calculated by Le field */ + uint16_t expected_res_size; /* Ne, calculated by Le field */ /* response APDU */ uint16_t sw; @@ -55,10 +55,10 @@ extern struct apdu apdu; #define EV_VERIFY_CMD_AVAILABLE ((eventmask_t)8) #define EV_MODIFY_CMD_AVAILABLE ((eventmask_t)16) -/* maximum cmd apdu data is key import 22+4+128+128 (proc_key_import) */ -#define MAX_CMD_APDU_SIZE (7+282) /* header + data */ -/* maximum res apdu data is public key 5+9+256+2 (gpg_do_public_key) */ -#define MAX_RES_APDU_SIZE ((5+9+256)+2) /* Data + status */ +/* Maximum cmd apdu data is key import 22+4+128+128 (proc_key_import) */ +#define MAX_CMD_APDU_DATA_SIZE (22+4+128+128) /* without header */ +/* Maximum res apdu data is public key 5+9+256 (gpg_do_public_key) */ +#define MAX_RES_APDU_DATA_SIZE (5+9+256) /* without trailer */ #define ICC_MSG_HEADER_SIZE 10 @@ -68,13 +68,6 @@ extern struct apdu apdu; /* USB buffer size of LL (Low-level): size of single Bulk transaction */ #define USB_LL_BUF_SIZE 64 -/* - * USB buffer size of USB-ICC driver - * (Changing this, dwMaxCCIDMessageLength too !!) - */ -#define USB_BUF_SIZE ((10 + 10 + MAX_CMD_APDU_SIZE + USB_LL_BUF_SIZE - 1) \ - / USB_LL_BUF_SIZE * USB_LL_BUF_SIZE) - enum icc_state { ICC_STATE_START, /* Initial */ @@ -85,7 +78,7 @@ enum icc_state ICC_STATE_SEND, /* APDU Sent Partially */ }; -extern volatile enum icc_state icc_state; +extern enum icc_state *icc_state_p; extern volatile uint8_t auth_status; #define AC_NONE_AUTHORIZED 0x00 @@ -116,7 +109,7 @@ extern void ac_reset_admin (void); extern void ac_fini (void); -extern void set_res_apdu (uint8_t sw1, uint8_t sw2); +extern void set_res_sw (uint8_t sw1, uint8_t sw2); extern uint16_t data_objects_number_of_bytes; extern void gpg_data_scan (const uint8_t *p); diff --git a/src/main.c b/src/main.c index c7460a8..5f62801 100644 --- a/src/main.c +++ b/src/main.c @@ -287,6 +287,13 @@ static void display_fatal_code (void) static void display_status_code (void) { + enum icc_state icc_state; + + if (icc_state_p == NULL) + icc_state = ICC_STATE_START; + else + icc_state = *icc_state_p; + if (icc_state == ICC_STATE_START) { set_led (1); diff --git a/src/openpgp-do.c b/src/openpgp-do.c index b91b10b..f0cd355 100644 --- a/src/openpgp-do.c +++ b/src/openpgp-do.c @@ -77,12 +77,11 @@ uint16_t data_objects_number_of_bytes; static const uint8_t historical_bytes[] __attribute__ ((aligned (1))) = { 10, 0x00, - 0x31, 0x80, /* Full DF name */ + 0x31, 0x84, /* Full DF name, GET DATA, MF */ 0x73, - 0x80, 0x01, 0x40, /* Full DF name */ + 0x80, 0x01, 0x80, /* Full DF name */ /* 1-byte */ - /* No command chaining */ - /* Extended Lc and Le */ + /* Command chaining, No extended Lc and Le */ 0x00, 0x90, 0x00 /* Status info (no life cycle management) */ }; @@ -90,27 +89,24 @@ static const uint8_t historical_bytes[] __attribute__ ((aligned (1))) = { static const uint8_t extended_capabilities[] __attribute__ ((aligned (1))) = { 10, 0x30, /* - * No SM, No get challenge, + * No SM, + * No get challenge, * Key import supported, * PW status byte can be put, * No private_use_DO, * No algo change allowed */ 0, /* Secure Messaging Algorithm: N/A (TDES=0, AES=1) */ - 0x00, 0x00, /* Max get challenge */ + 0x00, 0x00, /* Max get challenge (0: Get challenge not supported) */ #ifdef CERTDO_SUPPORT - 0x07, 0xfe, /* max. length of cardholder certificate (2KB - 2)*/ + 0x08, 0x00, /* max. length of cardholder certificate (2KiB) */ #else 0x00, 0x00, #endif - /* Max. length of command data */ - (MAX_CMD_APDU_SIZE>>8), (MAX_CMD_APDU_SIZE&0xff), + /* Max. length of command APDU */ + 0x00, 0xff, /* Max. length of response data */ -#ifdef CERTDO_SUPPORT - 0x08, 0x00, /* the case of cardholder ceritificate */ -#else - (MAX_RES_APDU_SIZE>>8), (MAX_RES_APDU_SIZE&0xff), -#endif + 0x00, 0xff, }; /* Algorithm Attributes */ diff --git a/src/openpgp.c b/src/openpgp.c index d73d773..eec46ee 100644 --- a/src/openpgp.c +++ b/src/openpgp.c @@ -67,7 +67,7 @@ select_file_TOP_result[] __attribute__ ((aligned (1))) = { }; void -set_res_apdu (uint8_t sw1, uint8_t sw2) +set_res_sw (uint8_t sw1, uint8_t sw2) { apdu.sw = (sw1 << 8) | sw2; } @@ -510,7 +510,7 @@ cmd_select_file (void) if (apdu.cmd_apdu_data_len != 6 || memcmp (openpgpcard_aid, apdu.cmd_apdu_data, 6) != 0) { - DEBUG_WORD (apdu.cmd_apdu_data_len); + DEBUG_SHORT (apdu.cmd_apdu_data_len); DEBUG_BINARY (apdu.cmd_apdu_data, apdu.cmd_apdu_data_len); GPG_NO_FILE (); @@ -528,6 +528,7 @@ cmd_select_file (void) res_APDU[1] = 0x12; res_APDU[2] = 0x84; /* overwrite: DF name */ res_APDU_size += 2; + GPG_SUCCESS (); } } else if (apdu.cmd_apdu_data_len == 2 diff --git a/src/openpgp.h b/src/openpgp.h index 8cd3f8c..b92c2c8 100644 --- a/src/openpgp.h +++ b/src/openpgp.h @@ -1,12 +1,12 @@ -#define GPG_MEMORY_FAILURE() set_res_apdu (0x65, 0x81) -#define GPG_SECURITY_FAILURE() set_res_apdu (0x69, 0x82) -#define GPG_SECURITY_AUTH_BLOCKED() set_res_apdu (0x69, 0x83) -#define GPG_CONDITION_NOT_SATISFIED() set_res_apdu (0x69, 0x85) -#define GPG_COMMAND_NOT_ALLOWED() set_res_apdu (0x69, 0x86) -#define GPG_FUNCTION_NOT_SUPPORTED() set_res_apdu (0x6a, 0x81) -#define GPG_NO_FILE() set_res_apdu (0x6a, 0x82) -#define GPG_NO_RECORD() set_res_apdu (0x6a, 0x88) -#define GPG_BAD_P0_P1() set_res_apdu (0x6b, 0x00) -#define GPG_NO_INS() set_res_apdu (0x6d, 0x00) -#define GPG_ERROR() set_res_apdu (0x6f, 0x00) -#define GPG_SUCCESS() set_res_apdu (0x90, 0x00) +#define GPG_MEMORY_FAILURE() set_res_sw (0x65, 0x81) +#define GPG_SECURITY_FAILURE() set_res_sw (0x69, 0x82) +#define GPG_SECURITY_AUTH_BLOCKED() set_res_sw (0x69, 0x83) +#define GPG_CONDITION_NOT_SATISFIED() set_res_sw (0x69, 0x85) +#define GPG_COMMAND_NOT_ALLOWED() set_res_sw (0x69, 0x86) +#define GPG_FUNCTION_NOT_SUPPORTED() set_res_sw (0x6a, 0x81) +#define GPG_NO_FILE() set_res_sw (0x6a, 0x82) +#define GPG_NO_RECORD() set_res_sw (0x6a, 0x88) +#define GPG_BAD_P0_P1() set_res_sw (0x6b, 0x00) +#define GPG_NO_INS() set_res_sw (0x6d, 0x00) +#define GPG_ERROR() set_res_sw (0x6f, 0x00) +#define GPG_SUCCESS() set_res_sw (0x90, 0x00) diff --git a/src/stmusb.mk b/src/stmusb.mk index f0b1b5e..b459268 100644 --- a/src/stmusb.mk +++ b/src/stmusb.mk @@ -3,5 +3,5 @@ STMUSBSRCDIR = $(STMUSBDIR)/src STMUSBINCDIR = $(STMUSBDIR)/inc STMUSBSRC= \ $(STMUSBSRCDIR)/usb_init.c $(STMUSBSRCDIR)/usb_int.c \ - $(STMUSBSRCDIR)/usb_mem.c $(STMUSBSRCDIR)/usb_core.c \ + $(STMUSBSRCDIR)/usb_core.c \ $(STMUSBSRCDIR)/usb_regs.c diff --git a/src/usb-icc.c b/src/usb-icc.c index db21ae8..f392fd8 100644 --- a/src/usb-icc.c +++ b/src/usb-icc.c @@ -31,8 +31,107 @@ #include "usb_istr.h" #include "usb_lld.h" +/* + * USB buffer size of USB-ICC driver + */ +#if MAX_RES_APDU_DATA_SIZE > MAX_CMD_APDU_DATA_SIZE +#define USB_BUF_SIZE (MAX_RES_APDU_DATA_SIZE+5) +#else +#define USB_BUF_SIZE (MAX_CMD_APDU_DATA_SIZE+5) +#endif + struct apdu apdu; +/* + * There are three layers in USB CCID implementation + * + * +-------------------+ + * | Application Layer | + * +-------------------+ + * ^ command APDU | + * | v response APDU + * +-------------------+ + * | CCID Layer | + * +-------------------+ + * ^ CCID PC_to_RDR | CCID RDR_to_PC + * | Message v Message + * +-------------------+ + * | USB Layer | + * +-------------------+ + * ^ USB | USB + * | Bulk-OUT Packet v Bulk-IN Packet + * + */ + +/* + * USB layer data structures + */ + +struct ep_in { + uint8_t ep_num; + uint8_t tx_done; + void (*notify) (struct ep_in *epi); + const uint8_t *buf; + size_t cnt; + size_t buf_len; + void *priv; + void (*next_buf) (struct ep_in *epi, size_t len); +}; + +static void epi_init (struct ep_in *epi, int ep_num, + void (*notify) (struct ep_in *epi), void *priv) +{ + epi->ep_num = ep_num; + epi->tx_done = 0; + epi->notify = notify; + epi->buf = NULL; + epi->cnt = 0; + epi->buf_len = 0; + epi->priv = priv; + epi->next_buf = NULL; +} + +struct ep_out { + uint8_t ep_num; + uint8_t err; + void (*notify) (struct ep_out *epo); + uint8_t *buf; + size_t cnt; + size_t buf_len; + void *priv; + void (*next_buf) (struct ep_out *epo, size_t len); + int (*end_rx) (struct ep_out *epo, size_t orig_len); +}; + +static struct ep_out endpoint_out; +static struct ep_in endpoint_in; + +static void epo_init (struct ep_out *epo, int ep_num, + void (*notify) (struct ep_out *epo), void *priv) +{ + epo->ep_num = ep_num; + epo->err = 0; + epo->notify = notify; + epo->buf = NULL; + epo->cnt = 0; + epo->buf_len = 0; + epo->priv = priv; + epo->next_buf = NULL; + epo->end_rx = NULL; +} + +/* + * CCID Layer + */ + +/* + * Buffer of USB communication: for both of RX and TX + * + * The buffer will be filled by multiple RX packets (Bulk-OUT) + * or will be used for multiple TX packets (Bulk-IN) + */ +static uint8_t icc_buffer[USB_BUF_SIZE]; + #define ICC_SET_PARAMS 0x61 /* non-ICCD command */ #define ICC_POWER_ON 0x62 #define ICC_POWER_OFF 0x63 @@ -49,7 +148,7 @@ struct apdu apdu; #define ICC_MSG_ERROR_OFFSET 8 #define ICC_MSG_CHAIN_OFFSET 9 #define ICC_MSG_DATA_OFFSET 10 /* == ICC_MSG_HEADER_SIZE */ -#define ICC_MAX_MSG_DATA_SIZE (USB_BUF_SIZE - ICC_MSG_HEADER_SIZE) +#define ICC_MAX_MSG_DATA_SIZE USB_BUF_SIZE #define ICC_STATUS_RUN 0x00 #define ICC_STATUS_PRESENT 0x01 @@ -65,21 +164,105 @@ struct apdu apdu; * error with offset 0 means "command not supported". */ #define ICC_OFFSET_CMD_NOT_SUPPORTED 0 +#define ICC_OFFSET_DATA_LEN 1 #define ICC_OFFSET_PARAM 8 struct icc_header { uint8_t msg_type; - int32_t data_len; + uint32_t data_len; uint8_t slot; uint8_t seq; uint8_t rsvd; uint16_t param; } __attribute__((packed)); -static int icc_data_size; + +enum icc_state *icc_state_p; + +/* Data structure handled by CCID layer */ +struct ccid { + enum icc_state icc_state; + uint8_t state; + uint8_t *p; + size_t len; + + uint8_t err; + + struct icc_header icc_header; + + uint8_t sw1sw2[2]; + + Thread *icc_thread; + Thread *application; + + /* lower layer */ + struct ep_out *epo; + struct ep_in *epi; + + /* upper layer */ + struct apdu *a; +}; /* - * USB-ICC communication could be considered "half duplex". + * APDU_STATE_WAIT_COMMAND +---------+ + * | | | | + * | v v | + * | APDU_STATE_COMMAND_CHAINING --+ + * | | + * v v + * APDU_STATE_COMMAND_RECEIVED + * | + * v + * =================== + * | Process COMMAND | + * =================== + * | + * v + * +-----+----------+ +---------+ + * | | | | + * v v v | + * APDU_STATE_RESULT <---- APDU_STATE_RESULT_GET_RESPONSE --+ + * | + * | + * v + * APDU_STATE_WAIT_COMMAND + */ + +#define APDU_STATE_WAIT_COMMAND 0 +#define APDU_STATE_COMMAND_CHAINING 1 +#define APDU_STATE_COMMAND_RECEIVED 2 +#define APDU_STATE_RESULT 3 +#define APDU_STATE_RESULT_GET_RESPONSE 4 + +static void ccid_init (struct ccid *c, struct ep_in *epi, struct ep_out *epo, + struct apdu *a, Thread *t) +{ + icc_state_p = &c->icc_state; + + c->icc_state = ICC_STATE_START; + c->state = APDU_STATE_WAIT_COMMAND; + /* + * Note: a is not yet initialized yet, we can't use c->a->cmd_apdu_data here. + */ + c->p = &icc_buffer[5]; + c->len = MAX_CMD_APDU_DATA_SIZE; + c->err = 0; + memset (&c->icc_header, 0, sizeof (struct icc_header)); + c->sw1sw2[0] = 0x90; + c->sw1sw2[1] = 0x00; + c->icc_thread = t; + c->application = NULL; + c->epi = epi; + c->epo = epo; + c->a = a; +} + +/* + * Application layer + */ + +/* + * USB-CCID communication could be considered "half duplex". * * While the device is sending something, there is no possibility for * the device to receive anything. @@ -88,152 +271,350 @@ static int icc_data_size; * for the device to send anything. * * Thus, the buffer can be shared for RX and TX. - */ - -/* - * Buffer of USB communication: for both of RX and TX * - * The buffer will be filled by multiple RX transactions (Bulk-OUT) - * or will be used for multiple TX transactions (Bulk-IN) + * Exception: When we support ABORT of CCID, it is possible to receive + * ABORT Class Specific Request to control pipe while we are + * receiving/sending something at OUT/IN endpoint. + * */ -static uint8_t icc_buffer[USB_BUF_SIZE]; -static uint8_t icc_seq; -/* - * Pointer to ICC_BUFFER - */ -static uint8_t *icc_next_p; +#define CMD_APDU_HEAD_SIZE 5 -/* - * Chain pointer: This implementation support two packets in chain (not more) - */ -static uint8_t *icc_chain_p; +static void apdu_init (struct apdu *a) +{ + a->seq = 0; /* will be set by lower layer */ + a->cmd_apdu_head = &icc_buffer[0]; + a->cmd_apdu_data = &icc_buffer[5]; + a->cmd_apdu_data_len = 0; /* will be set by lower layer */ + a->expected_res_size = 0; /* will be set by lower layer */ -/* - * Whole size of TX transfer (Bulk-IN transactions) - */ -static int icc_tx_size; - -static Thread *icc_thread; + a->sw = 0x9000; /* will be set by upper layer */ + a->res_apdu_data = &icc_buffer[5]; /* will be set by upper layer */ + a->res_apdu_data_len = 0; /* will be set by upper layer */ +} #define EV_RX_DATA_READY (eventmask_t)1 /* USB Rx data available */ /* EV_EXEC_FINISHED == 2 */ #define EV_TX_FINISHED (eventmask_t)4 /* USB Tx finished */ + +static void notify_tx (struct ep_in *epi) +{ + struct ccid *c = (struct ccid *)epi->priv; + + /* The sequence of Bulk-IN transactions finished */ + chEvtSignalI (c->icc_thread, EV_TX_FINISHED); +} + +static void no_buf (struct ep_in *epi, size_t len) +{ + (void)len; + epi->buf = NULL; + epi->cnt = 0; + epi->buf_len = 0; +} + +static void set_sw1sw2 (struct ep_in *epi) +{ + struct ccid *c = (struct ccid *)epi->priv; + + if (c->a->expected_res_size >= c->len) + { + c->sw1sw2[0] = 0x90; + c->sw1sw2[1] = 0x00; + } + else + { + c->sw1sw2[0] = 0x61; + if (c->len >= 256) + c->sw1sw2[1] = 0; + else + c->sw1sw2[1] = (uint8_t)c->len; + } +} + +static void get_sw1sw2 (struct ep_in *epi, size_t len) +{ + struct ccid *c = (struct ccid *)epi->priv; + + (void)len; + epi->buf = c->sw1sw2; + epi->cnt = 0; + epi->buf_len = 2; + epi->next_buf = no_buf; +} + + /* * Tx done callback */ void EP1_IN_Callback (void) { - if (icc_next_p == NULL) - /* The sequence of Bulk-IN transactions finished */ - chEvtSignalI (icc_thread, EV_TX_FINISHED); - else if (icc_next_p == &icc_buffer[icc_tx_size]) - /* It was multiple of USB_LL_BUF_SIZE */ - { - /* Send the last 0-DATA transcation of Bulk-IN in the transactions */ - icc_next_p = NULL; - usb_lld_write (ENDP1, icc_buffer, 0); - } + struct ep_in *epi = &endpoint_in; + + if (epi->buf == NULL) + if (epi->tx_done) + epi->notify (epi); + else + { + epi->tx_done = 1; + usb_lld_tx_enable (epi->ep_num, 0); /* send ZLP */ + } else { - int tx_size = USB_LL_BUF_SIZE; - uint8_t *p = icc_next_p; + int tx_size = 0; + size_t remain = USB_LL_BUF_SIZE; + int offset = 0; - icc_next_p += USB_LL_BUF_SIZE; - if (icc_next_p > &icc_buffer[icc_tx_size]) - { - icc_next_p = NULL; - tx_size = &icc_buffer[icc_tx_size] - p; - } + while (epi->buf) + if (epi->buf_len < remain) + { + usb_lld_txcpy (epi->buf, epi->ep_num, offset, epi->buf_len); + offset += epi->buf_len; + remain -= epi->buf_len; + tx_size += epi->buf_len; + epi->next_buf (epi, remain); /* Update epi->buf, cnt, buf_len */ + } + else + { + usb_lld_txcpy (epi->buf, epi->ep_num, offset, remain); + epi->buf += remain; + epi->cnt += remain; + epi->buf_len -= remain; + tx_size += remain; + break; + } - usb_lld_write (ENDP1, p, tx_size); + if (tx_size < USB_LL_BUF_SIZE) + epi->tx_done = 1; + usb_lld_tx_enable (epi->ep_num, tx_size); } } -static void -icc_prepare_receive (int chain) -{ - if (chain) - icc_next_p = icc_chain_p; - else - icc_next_p = icc_buffer; - usb_lld_rx_enable (ENDP2); +static void notify_icc (struct ep_out *epo) +{ + struct ccid *c = (struct ccid *)epo->priv; + + c->err = epo->err; + chEvtSignalI (c->icc_thread, EV_RX_DATA_READY); +} + +static int end_icc_rx (struct ep_out *epo, size_t orig_len) +{ + (void)orig_len; + if (epo->cnt < sizeof (struct icc_header)) + /* short packet, just ignore */ + return 1; + + /* icc message with no abdata */ + return 0; +} + +static int end_notify (struct ep_out *epo, size_t orig_len) +{ + (void)epo; + (void)orig_len; + return 0; +} + +static int end_cmd_apdu_head (struct ep_out *epo, size_t orig_len) +{ + struct ccid *c = (struct ccid *)epo->priv; + + (void)orig_len; + + if (epo->cnt < 4 || epo->cnt != c->icc_header.data_len) + epo->err = 1; + + if (epo->cnt == 4) + { + /* No Lc and Le */ + c->a->cmd_apdu_data_len = 0; + c->a->expected_res_size = 0; + } + else if (epo->cnt == 5) + { + /* No Lc but Le */ + c->a->cmd_apdu_data_len = 0; + c->a->expected_res_size = c->a->cmd_apdu_head[4]; + if (c->a->expected_res_size == 0) + c->a->expected_res_size = 256; + c->a->cmd_apdu_head[4] = 0; + } + + return 0; +} + + +static int end_nomore_data (struct ep_out *epo, size_t orig_len) +{ + (void)epo; + if (orig_len == USB_LL_BUF_SIZE) + return 1; + else + return 0; +} + + +static int end_cmd_apdu_data (struct ep_out *epo, size_t orig_len) +{ + struct ccid *c = (struct ccid *)epo->priv; + size_t len = epo->cnt; + + if (orig_len == USB_LL_BUF_SIZE + && CMD_APDU_HEAD_SIZE + len < c->icc_header.data_len) + /* more packet comes */ + return 1; + + if (CMD_APDU_HEAD_SIZE + len != c->icc_header.data_len) + goto error; + + if (len == c->a->cmd_apdu_head[4]) + /* No Le field*/ + c->a->expected_res_size = 0; + else if (len == (size_t)c->a->cmd_apdu_head[4] + 1) + { + /* it has Le field*/ + c->a->expected_res_size = epo->buf[-1]; + len--; + } + else + { + error: + epo->err = 1; + return 0; + } + + c->a->cmd_apdu_data_len += len; + return 0; +} + + +static void nomore_data (struct ep_out *epo, size_t len) +{ + (void)len; + epo->err = 1; + epo->end_rx = end_nomore_data; + epo->buf = NULL; + epo->buf_len = 0; + epo->cnt = 0; + epo->next_buf = nomore_data; +} + +#define INS_GET_RESPONSE 0xc0 + +static void icc_cmd_apdu_data (struct ep_out *epo, size_t len) +{ + struct ccid *c = (struct ccid *)epo->priv; + + (void)len; + if (c->state == APDU_STATE_RESULT_GET_RESPONSE + && c->a->cmd_apdu_head[1] != INS_GET_RESPONSE) + { + /* + * Handling exceptional request. + * + * Host didn't finish receiving the whole response APDU by GET RESPONSE, + * but initiates another command. + */ + + c->state = APDU_STATE_WAIT_COMMAND; + c->p = c->a->cmd_apdu_data; + c->len = MAX_CMD_APDU_DATA_SIZE; + } + + epo->end_rx = end_cmd_apdu_data; + epo->buf = c->p; + epo->buf_len = c->len; + epo->cnt = 0; + epo->next_buf = nomore_data; +} + +static void icc_abdata (struct ep_out *epo, size_t len) +{ + struct ccid *c = (struct ccid *)epo->priv; + + (void)len; + if (c->icc_header.msg_type == ICC_XFR_BLOCK) + { + c->a->seq = c->icc_header.seq; + epo->end_rx = end_cmd_apdu_head; + epo->buf = c->a->cmd_apdu_head; + epo->buf_len = 5; + epo->cnt = 0; + epo->next_buf = icc_cmd_apdu_data; + } + else + { + epo->end_rx = end_notify; + epo->buf = c->p; + epo->buf_len = c->len; + epo->cnt = 0; + epo->next_buf = nomore_data; + } +} + + +static void +icc_prepare_receive (struct ccid *c) +{ + DEBUG_INFO ("Rx ready\r\n"); + + c->epo->err = 0; + c->epo->buf = (uint8_t *)&c->icc_header; + c->epo->buf_len = sizeof (struct icc_header); + c->epo->cnt = 0; + c->epo->next_buf = icc_abdata; + c->epo->end_rx = end_icc_rx; + usb_lld_rx_enable (c->epo->ep_num); } /* * Rx ready callback */ + void EP2_OUT_Callback (void) { - int len; - struct icc_header *icc_header; - int data_len_so_far; - int data_len; + struct ep_out *epo = &endpoint_out; + size_t len = usb_lld_rx_data_len (epo->ep_num); + int offset = 0; + int cont; + size_t orig_len = len; - len = usb_lld_rx_data_len (ENDP2); - usb_lld_rxcpy (icc_next_p, ENDP2, 0, len); - if (len == 0) - { /* Just ignore Zero Length Packet (ZLP), if any */ - usb_lld_rx_enable (ENDP2); - return; - } + while (epo->err == 0) + if (len == 0) + break; + else if (len <= epo->buf_len) + { + usb_lld_rxcpy (epo->buf, epo->ep_num, offset, len); + epo->buf += len; + epo->cnt += len; + epo->buf_len -= len; + break; + } + else /* len > buf_len */ + { + usb_lld_rxcpy (epo->buf, epo->ep_num, offset, epo->buf_len); + len -= epo->buf_len; + offset += epo->buf_len; + epo->next_buf (epo, len); /* Update epo->buf, cnt, buf_len */ + } - icc_next_p += len; + /* + * ORIG_LEN to distingush ZLP and the end of transaction + * (ORIG_LEN != USB_LL_BUF_SIZE) + */ + cont = epo->end_rx (epo, orig_len); - if (icc_chain_p) - icc_header = (struct icc_header *)icc_chain_p; + if (cont) + usb_lld_rx_enable (epo->ep_num); else - icc_header = (struct icc_header *)icc_buffer; - - data_len = icc_header->data_len; /* NOTE: We're little endian */ - data_len_so_far = (icc_next_p - (uint8_t *)icc_header) - ICC_MSG_HEADER_SIZE; - - if (len == USB_LL_BUF_SIZE - && data_len != data_len_so_far) - /* The sequence of transactions continues */ - { - usb_lld_rx_enable (ENDP2); - if ((icc_next_p - icc_buffer) >= USB_BUF_SIZE) - /* No room to receive any more */ - { - DEBUG_INFO ("ERR0F\r\n"); - icc_next_p -= USB_LL_BUF_SIZE; /* Just for not overrun the buffer */ - /* - * Receive until the end of the sequence - * (and discard the whole block) - */ - } - - /* - * NOTE: It is possible a transaction may stall when the size of - * BULK_OUT transaction it's bigger than USB_BUF_SIZE and stops - * with just USB_LL_BUF_SIZE packet. Device will remain waiting - * another packet. - */ - } - else /* Finished */ - { - icc_data_size = data_len_so_far; - icc_seq = icc_header->seq; /* NOTE: We're little endian */ - - if (icc_data_size != data_len) - { - DEBUG_INFO ("ERR0E\r\n"); - /* Ignore the whole block */ - icc_chain_p = NULL; - icc_prepare_receive (0); - } - else - /* Notify myself */ - chEvtSignalI (icc_thread, EV_RX_DATA_READY); - } + epo->notify (epo); } -volatile enum icc_state icc_state; /* * ATR (Answer To Reset) string @@ -254,32 +635,26 @@ volatile enum icc_state icc_state; * Minimum: 0x3b, 0x8a, 0x80, 0x01, + historical bytes, xor check * */ -static const char ATR[] = { +static const uint8_t ATR[] = { 0x3b, 0xda, 0x11, 0xff, 0x81, 0xb1, 0xfe, 0x55, 0x1f, 0x03, 0x00, 0x31, 0x84, /* full DF name, GET DATA, MF */ 0x73, - 0x80, /* DF full name */ - 0x01, /* 1-byte */ - 0x40, /* Extended Lc and extended Le */ + 0x80, /* DF full name */ + 0x01, /* 1-byte */ + 0x80, /* Command chaining, No extended Lc and extended Le */ 0x00, 0x90, 0x00, (0xda^0x11^0xff^0x81^0xb1^0xfe^0x55^0x1f^0x03 - ^0x00^0x31^0x84^0x73^0x80^0x01^0x40^0x00^0x90^0x00) + ^0x00^0x31^0x84^0x73^0x80^0x01^0x80^0x00^0x90^0x00) }; /* * Send back error */ -void -icc_error (int offset) +static void icc_error (struct ccid *c, int offset) { - uint8_t *icc_reply; - - if (icc_chain_p) - icc_reply = icc_chain_p; - else - icc_reply = icc_buffer; + uint8_t icc_reply[ICC_MSG_HEADER_SIZE]; icc_reply[0] = ICC_SLOT_STATUS_RET; /* Any value should be OK */ icc_reply[1] = 0x00; @@ -287,8 +662,8 @@ icc_error (int offset) icc_reply[3] = 0x00; icc_reply[4] = 0x00; icc_reply[5] = 0x00; /* Slot */ - icc_reply[ICC_MSG_SEQ_OFFSET] = icc_seq; - if (icc_state == ICC_STATE_START) + icc_reply[ICC_MSG_SEQ_OFFSET] = c->icc_header.seq; + if (c->icc_state == ICC_STATE_START) /* 1: ICC present but not activated 2: No ICC present */ icc_reply[ICC_MSG_STATUS_OFFSET] = 1; else @@ -298,56 +673,55 @@ icc_error (int offset) icc_reply[ICC_MSG_ERROR_OFFSET] = offset; icc_reply[ICC_MSG_CHAIN_OFFSET] = 0x00; - icc_next_p = NULL; /* This is a single transaction Bulk-IN */ - icc_tx_size = ICC_MSG_HEADER_SIZE; - usb_lld_write (ENDP1, icc_reply, icc_tx_size); + /* This is a single packet Bulk-IN transaction */ + c->epi->buf = NULL; + c->epi->tx_done = 1; + usb_lld_write (c->epi->ep_num, icc_reply, ICC_MSG_HEADER_SIZE); } -static Thread *gpg_thread; static WORKING_AREA(waGPGthread, 128*16); extern msg_t GPGthread (void *arg); /* Send back ATR (Answer To Reset) */ enum icc_state -icc_power_on (void) +icc_power_on (struct ccid *c) { - int size_atr; + size_t size_atr = sizeof (ATR); + uint8_t p[ICC_MSG_HEADER_SIZE]; - if (gpg_thread == NULL) - gpg_thread = chThdCreateStatic (waGPGthread, sizeof(waGPGthread), - NORMALPRIO, GPGthread, (void *)icc_thread); + if (c->application == NULL) + c->application = chThdCreateStatic (waGPGthread, sizeof(waGPGthread), + NORMALPRIO, GPGthread, + (void *)c->icc_thread); - size_atr = sizeof (ATR); - icc_buffer[0] = ICC_DATA_BLOCK_RET; - icc_buffer[1] = size_atr; - icc_buffer[2] = 0x00; - icc_buffer[3] = 0x00; - icc_buffer[4] = 0x00; - icc_buffer[5] = 0x00; /* Slot */ - icc_buffer[ICC_MSG_SEQ_OFFSET] = icc_seq; - icc_buffer[ICC_MSG_STATUS_OFFSET] = 0x00; - icc_buffer[ICC_MSG_ERROR_OFFSET] = 0x00; - icc_buffer[ICC_MSG_CHAIN_OFFSET] = 0x00; - memcpy (&icc_buffer[ICC_MSG_DATA_OFFSET], ATR, size_atr); + p[0] = ICC_DATA_BLOCK_RET; + p[1] = size_atr; + p[2] = 0x00; + p[3] = 0x00; + p[4] = 0x00; + p[5] = 0x00; /* Slot */ + p[ICC_MSG_SEQ_OFFSET] = c->icc_header.seq; + p[ICC_MSG_STATUS_OFFSET] = 0x00; + p[ICC_MSG_ERROR_OFFSET] = 0x00; + p[ICC_MSG_CHAIN_OFFSET] = 0x00; - icc_next_p = NULL; /* This is a single transaction Bulk-IN */ - icc_tx_size = ICC_MSG_HEADER_SIZE + size_atr; - usb_lld_write (ENDP1, icc_buffer, icc_tx_size); + usb_lld_txcpy (p, c->epi->ep_num, 0, ICC_MSG_HEADER_SIZE); + usb_lld_txcpy (ATR, c->epi->ep_num, ICC_MSG_HEADER_SIZE, size_atr); + + /* This is a single packet Bulk-IN transaction */ + c->epi->buf = NULL; + c->epi->tx_done = 1; + usb_lld_tx_enable (c->epi->ep_num, ICC_MSG_HEADER_SIZE + size_atr); DEBUG_INFO ("ON\r\n"); return ICC_STATE_WAIT; } static void -icc_send_status (void) +icc_send_status (struct ccid *c) { - uint8_t *icc_reply; - - if (icc_chain_p) - icc_reply = icc_chain_p; - else - icc_reply = icc_buffer; + uint8_t icc_reply[ICC_MSG_HEADER_SIZE]; icc_reply[0] = ICC_SLOT_STATUS_RET; icc_reply[1] = 0x00; @@ -355,8 +729,8 @@ icc_send_status (void) icc_reply[3] = 0x00; icc_reply[4] = 0x00; icc_reply[5] = 0x00; /* Slot */ - icc_reply[ICC_MSG_SEQ_OFFSET] = icc_seq; - if (icc_state == ICC_STATE_START) + icc_reply[ICC_MSG_SEQ_OFFSET] = c->icc_header.seq; + if (c->icc_state == ICC_STATE_START) /* 1: ICC present but not activated 2: No ICC present */ icc_reply[ICC_MSG_STATUS_OFFSET] = 1; else @@ -365,9 +739,10 @@ icc_send_status (void) icc_reply[ICC_MSG_ERROR_OFFSET] = 0x00; icc_reply[ICC_MSG_CHAIN_OFFSET] = 0x00; - icc_next_p = NULL; /* This is a single transaction Bulk-IN */ - icc_tx_size = ICC_MSG_HEADER_SIZE; - usb_lld_write (ENDP1, icc_reply, icc_tx_size); + /* This is a single packet Bulk-IN transaction */ + c->epi->buf = NULL; + c->epi->tx_done = 1; + usb_lld_write (c->epi->ep_num, icc_reply, ICC_MSG_HEADER_SIZE); #ifdef DEBUG_MORE DEBUG_INFO ("St\r\n"); @@ -375,34 +750,27 @@ icc_send_status (void) } enum icc_state -icc_power_off (void) +icc_power_off (struct ccid *c) { - icc_data_size = 0; - - if (gpg_thread) + if (c->application) { - chThdTerminate (gpg_thread); - chEvtSignal (gpg_thread, EV_NOP); - chThdWait (gpg_thread); - gpg_thread = NULL; + chThdTerminate (c->application); + chEvtSignal (c->application, EV_NOP); + chThdWait (c->application); + c->application = NULL; } - icc_state = ICC_STATE_START; /* This status change should be here */ - icc_send_status (); + c->icc_state = ICC_STATE_START; /* This status change should be here */ + icc_send_status (c); DEBUG_INFO ("OFF\r\n"); return ICC_STATE_START; } static void -icc_send_data_block (int len, uint8_t status, uint8_t chain) +icc_send_data_block_0x9000 (struct ccid *c) { - int tx_size = USB_LL_BUF_SIZE; - uint8_t *p; - - if (icc_chain_p) - p = icc_chain_p; - else - p = icc_buffer; + uint8_t p[ICC_MSG_HEADER_SIZE+2]; + size_t len = 2; p[0] = ICC_DATA_BLOCK_RET; p[1] = len & 0xFF; @@ -410,415 +778,400 @@ icc_send_data_block (int len, uint8_t status, uint8_t chain) p[3] = (len >> 16)& 0xFF; p[4] = (len >> 24)& 0xFF; p[5] = 0x00; /* Slot */ - p[ICC_MSG_SEQ_OFFSET] = icc_seq; - p[ICC_MSG_STATUS_OFFSET] = status; + p[ICC_MSG_SEQ_OFFSET] = c->a->seq; + p[ICC_MSG_STATUS_OFFSET] = 0; p[ICC_MSG_ERROR_OFFSET] = 0; - p[ICC_MSG_CHAIN_OFFSET] = chain; + p[ICC_MSG_CHAIN_OFFSET] = 0; + p[ICC_MSG_CHAIN_OFFSET+1] = 0x90; + p[ICC_MSG_CHAIN_OFFSET+2] = 0x00; - icc_tx_size = ICC_MSG_HEADER_SIZE + len; - if (icc_tx_size < USB_LL_BUF_SIZE) - { - icc_next_p = NULL; - tx_size = icc_tx_size; - } - else - icc_next_p = p + USB_LL_BUF_SIZE; + usb_lld_txcpy (p, c->epi->ep_num, 0, ICC_MSG_HEADER_SIZE + len); + c->epi->buf = NULL; + c->epi->tx_done = 1; - usb_lld_write (ENDP1, p, tx_size); + usb_lld_tx_enable (c->epi->ep_num, ICC_MSG_HEADER_SIZE + len); #ifdef DEBUG_MORE DEBUG_INFO ("DATA\r\n"); #endif } static void -icc_send_params (void) +icc_send_data_block (struct ccid *c, uint8_t status) { - icc_buffer[0] = ICC_PARAMS_RET; - icc_buffer[1] = 0x07; /* Length = 0x00000007 */ - icc_buffer[2] = 0; - icc_buffer[3] = 0; - icc_buffer[4] = 0; - icc_buffer[5] = 0x00; /* Slot */ - icc_buffer[ICC_MSG_SEQ_OFFSET] = icc_seq; - icc_buffer[ICC_MSG_STATUS_OFFSET] = 0; - icc_buffer[ICC_MSG_ERROR_OFFSET] = 0; - icc_buffer[ICC_MSG_CHAIN_OFFSET] = 0x01; /* ProtocolNum: T=1 */ - icc_buffer[ICC_MSG_DATA_OFFSET] = 0x11; /* bmFindexDindex */ - icc_buffer[ICC_MSG_DATA_OFFSET+1] = 0x11; /* bmTCCKST1 */ - icc_buffer[ICC_MSG_DATA_OFFSET+2] = 0xFE; /* bGuardTimeT1 */ - icc_buffer[ICC_MSG_DATA_OFFSET+3] = 0x55; /* bmWaitingIntegersT1 */ - icc_buffer[ICC_MSG_DATA_OFFSET+4] = 0x03; /* bClockStop */ - icc_buffer[ICC_MSG_DATA_OFFSET+5] = 0xFE; /* bIFSC */ - icc_buffer[ICC_MSG_DATA_OFFSET+6] = 0; /* bNadValue */ + int tx_size = USB_LL_BUF_SIZE; + uint8_t p[ICC_MSG_HEADER_SIZE]; + size_t len; - icc_next_p = NULL; /* This is a single transaction Bulk-IN */ - icc_tx_size = ICC_MSG_HEADER_SIZE + 7; - usb_lld_write (ENDP1, icc_buffer, icc_tx_size); + if (status == 0) + len = c->a->res_apdu_data_len + 2; + else + len = 0; + + p[0] = ICC_DATA_BLOCK_RET; + p[1] = len & 0xFF; + p[2] = (len >> 8)& 0xFF; + p[3] = (len >> 16)& 0xFF; + p[4] = (len >> 24)& 0xFF; + p[5] = 0x00; /* Slot */ + p[ICC_MSG_SEQ_OFFSET] = c->a->seq; + p[ICC_MSG_STATUS_OFFSET] = status; + p[ICC_MSG_ERROR_OFFSET] = 0; + p[ICC_MSG_CHAIN_OFFSET] = 0; + + usb_lld_txcpy (p, c->epi->ep_num, 0, ICC_MSG_HEADER_SIZE); + if (len == 0) + { + usb_lld_tx_enable (c->epi->ep_num, ICC_MSG_HEADER_SIZE); + return; + } + + if (ICC_MSG_HEADER_SIZE + len <= USB_LL_BUF_SIZE) + { + usb_lld_txcpy (c->a->res_apdu_data, c->epi->ep_num, + ICC_MSG_HEADER_SIZE, c->a->res_apdu_data_len); + usb_lld_txcpy (c->sw1sw2, c->epi->ep_num, + ICC_MSG_HEADER_SIZE + c->a->res_apdu_data_len, 2); + c->epi->buf = NULL; + if (ICC_MSG_HEADER_SIZE + len < USB_LL_BUF_SIZE) + c->epi->tx_done = 1; + tx_size = ICC_MSG_HEADER_SIZE + len; + } + else if (ICC_MSG_HEADER_SIZE + len - 1 == USB_LL_BUF_SIZE) + { + usb_lld_txcpy (c->a->res_apdu_data, c->epi->ep_num, + ICC_MSG_HEADER_SIZE, c->a->res_apdu_data_len); + usb_lld_txcpy (c->sw1sw2, c->epi->ep_num, + ICC_MSG_HEADER_SIZE + c->a->res_apdu_data_len, 1); + c->epi->buf = &c->sw1sw2[1]; + c->epi->cnt = 1; + c->epi->buf_len = 1; + c->epi->next_buf = no_buf; + } + else if (ICC_MSG_HEADER_SIZE + len - 2 == USB_LL_BUF_SIZE) + { + usb_lld_txcpy (c->a->res_apdu_data, c->epi->ep_num, + ICC_MSG_HEADER_SIZE, c->a->res_apdu_data_len); + c->epi->buf = &c->sw1sw2[0]; + c->epi->cnt = 0; + c->epi->buf_len = 2; + c->epi->next_buf = no_buf; + } + else + { + usb_lld_txcpy (c->a->res_apdu_data, c->epi->ep_num, ICC_MSG_HEADER_SIZE, + USB_LL_BUF_SIZE - ICC_MSG_HEADER_SIZE); + c->epi->buf = c->a->res_apdu_data + USB_LL_BUF_SIZE - ICC_MSG_HEADER_SIZE; + c->epi->cnt = USB_LL_BUF_SIZE - ICC_MSG_HEADER_SIZE; + c->epi->buf_len = c->a->res_apdu_data_len + - (USB_LL_BUF_SIZE - ICC_MSG_HEADER_SIZE); + c->epi->next_buf = get_sw1sw2; + } + + usb_lld_tx_enable (c->epi->ep_num, tx_size); #ifdef DEBUG_MORE DEBUG_INFO ("DATA\r\n"); #endif } -/* Supporting smaller buffer of libccid (<= 1.3.11) */ -#define ICC_RESPONSE_MSG_DATA_SIZE 262 +static void +icc_send_data_block_gr (struct ccid *c, size_t chunk_len) +{ + int tx_size = USB_LL_BUF_SIZE; + uint8_t p[ICC_MSG_HEADER_SIZE]; + size_t len = chunk_len + 2; + + p[0] = ICC_DATA_BLOCK_RET; + p[1] = len & 0xFF; + p[2] = (len >> 8)& 0xFF; + p[3] = (len >> 16)& 0xFF; + p[4] = (len >> 24)& 0xFF; + p[5] = 0x00; /* Slot */ + p[ICC_MSG_SEQ_OFFSET] = c->a->seq; + p[ICC_MSG_STATUS_OFFSET] = 0; + p[ICC_MSG_ERROR_OFFSET] = 0; + p[ICC_MSG_CHAIN_OFFSET] = 0; + + usb_lld_txcpy (p, c->epi->ep_num, 0, ICC_MSG_HEADER_SIZE); + + set_sw1sw2 (c->epi); + + if (chunk_len <= USB_LL_BUF_SIZE - ICC_MSG_HEADER_SIZE) + { + int size_for_sw; + + if (chunk_len <= USB_LL_BUF_SIZE - ICC_MSG_HEADER_SIZE - 2) + size_for_sw = 2; + else if (chunk_len == USB_LL_BUF_SIZE - ICC_MSG_HEADER_SIZE - 1) + size_for_sw = 1; + else + size_for_sw = 0; + + usb_lld_txcpy (c->p, c->epi->ep_num, ICC_MSG_HEADER_SIZE, chunk_len); + if (size_for_sw) + usb_lld_txcpy (c->sw1sw2, c->epi->ep_num, + ICC_MSG_HEADER_SIZE + chunk_len, size_for_sw); + tx_size = ICC_MSG_HEADER_SIZE + chunk_len + size_for_sw; + if (size_for_sw == 2) + { + c->epi->buf = NULL; + if (tx_size < USB_LL_BUF_SIZE) + c->epi->tx_done = 1; + /* Don't set epi->tx_done = 1, when it requires ZLP */ + } + else + { + c->epi->buf = c->sw1sw2 + size_for_sw; + c->epi->cnt = size_for_sw; + c->epi->buf_len = 2 - size_for_sw; + c->epi->next_buf = no_buf; + } + } + else + { + usb_lld_txcpy (c->p, c->epi->ep_num, ICC_MSG_HEADER_SIZE, + USB_LL_BUF_SIZE - ICC_MSG_HEADER_SIZE); + c->epi->buf = c->p + USB_LL_BUF_SIZE - ICC_MSG_HEADER_SIZE; + c->epi->cnt = 0; + c->epi->buf_len = chunk_len - (USB_LL_BUF_SIZE - ICC_MSG_HEADER_SIZE); + c->epi->next_buf = get_sw1sw2; + } + + c->p += chunk_len; + c->len -= chunk_len; + usb_lld_tx_enable (c->epi->ep_num, tx_size); +#ifdef DEBUG_MORE + DEBUG_INFO ("DATA\r\n"); +#endif +} + + +static void +icc_send_params (struct ccid *c) +{ + uint8_t p[ICC_MSG_HEADER_SIZE]; + const uint8_t params[] = { + 0x11, /* bmFindexDindex */ + 0x11, /* bmTCCKST1 */ + 0xFE, /* bGuardTimeT1 */ + 0x55, /* bmWaitingIntegersT1 */ + 0x03, /* bClockStop */ + 0xFE, /* bIFSC */ + 0 /* bNadValue */ + }; + + p[0] = ICC_PARAMS_RET; + p[1] = 0x07; /* Length = 0x00000007 */ + p[2] = 0; + p[3] = 0; + p[4] = 0; + p[5] = 0x00; /* Slot */ + p[ICC_MSG_SEQ_OFFSET] = c->icc_header.seq; + p[ICC_MSG_STATUS_OFFSET] = 0; + p[ICC_MSG_ERROR_OFFSET] = 0; + p[ICC_MSG_CHAIN_OFFSET] = 0x01; /* ProtocolNum: T=1 */ + + usb_lld_txcpy (p, c->epi->ep_num, 0, ICC_MSG_HEADER_SIZE); + usb_lld_txcpy (params, c->epi->ep_num, ICC_MSG_HEADER_SIZE, sizeof params); + + /* This is a single packet Bulk-IN transaction */ + c->epi->buf = NULL; + c->epi->tx_done = 1; + usb_lld_tx_enable (c->epi->ep_num, ICC_MSG_HEADER_SIZE + sizeof params); +#ifdef DEBUG_MORE + DEBUG_INFO ("PARAMS\r\n"); +#endif +} + static enum icc_state -icc_handle_data (void) +icc_handle_data (struct ccid *c) { - enum icc_state next_state = icc_state; - struct icc_header *icc_header; + enum icc_state next_state = c->icc_state; - if (icc_chain_p) - icc_header = (struct icc_header *)icc_chain_p; - else - icc_header = (struct icc_header *)icc_buffer; + if (c->err != 0) + { + c->err = 0; + c->state = APDU_STATE_WAIT_COMMAND; + c->p = c->a->cmd_apdu_data; + c->len = MAX_CMD_APDU_DATA_SIZE; + c->a->cmd_apdu_data_len = 0; + c->a->expected_res_size = 0; + icc_error (c, ICC_OFFSET_DATA_LEN); + return next_state; + } - switch (icc_state) + switch (c->icc_state) { case ICC_STATE_START: - if (icc_header->msg_type == ICC_POWER_ON) - next_state = icc_power_on (); - else if (icc_header->msg_type == ICC_POWER_OFF) - next_state = icc_power_off (); - else if (icc_header->msg_type == ICC_SLOT_STATUS) - icc_send_status (); + if (c->icc_header.msg_type == ICC_POWER_ON) + next_state = icc_power_on (c); + else if (c->icc_header.msg_type == ICC_POWER_OFF) + next_state = icc_power_off (c); + else if (c->icc_header.msg_type == ICC_SLOT_STATUS) + icc_send_status (c); else { DEBUG_INFO ("ERR01\r\n"); - icc_error (ICC_OFFSET_CMD_NOT_SUPPORTED); + icc_error (c, ICC_OFFSET_CMD_NOT_SUPPORTED); } break; case ICC_STATE_WAIT: - if (icc_header->msg_type == ICC_POWER_ON) + if (c->icc_header.msg_type == ICC_POWER_ON) /* Not in the spec., but pcscd/libccid */ - next_state = icc_power_on (); - else if (icc_header->msg_type == ICC_POWER_OFF) - next_state = icc_power_off (); - else if (icc_header->msg_type == ICC_SLOT_STATUS) - icc_send_status (); - else if (icc_header->msg_type == ICC_XFR_BLOCK) + next_state = icc_power_on (c); + else if (c->icc_header.msg_type == ICC_POWER_OFF) + next_state = icc_power_off (c); + else if (c->icc_header.msg_type == ICC_SLOT_STATUS) + icc_send_status (c); + else if (c->icc_header.msg_type == ICC_XFR_BLOCK) { - if (icc_header->param == 0) - { /* Give this message to GPG thread */ - run_gpg_thread: - apdu.seq = icc_seq; - apdu.cmd_apdu_head = &icc_buffer[ICC_MSG_HEADER_SIZE]; - - apdu.sw = 0x9000; - apdu.res_apdu_data_len = 0; - apdu.res_apdu_data = &icc_buffer[ICC_MSG_HEADER_SIZE]; - - if (icc_data_size < 4) - { - icc_error (ICC_MSG_HEADER_SIZE + icc_data_size); - next_state = ICC_STATE_WAIT; - break; - } - else if (icc_data_size == 4) - { /* No Lc and No Le */ - apdu.cmd_apdu_data = NULL; - apdu.cmd_apdu_data_len = 0; - apdu.expected_res_size = 0; - apdu.cmd_apdu_head[4] = 0; - } - else if (icc_data_size == 5) - { /* No Lc but Le */ - apdu.cmd_apdu_data = NULL; - apdu.cmd_apdu_data_len = 0; - apdu.expected_res_size = apdu.cmd_apdu_head[4]; - if (apdu.expected_res_size == 0) - apdu.expected_res_size = 256; - apdu.cmd_apdu_head[4] = 0; - } - else if (apdu.cmd_apdu_head[4] == 0) - { /* extended Lc or extended Le */ - apdu.cmd_apdu_data = apdu.cmd_apdu_head + 7; - apdu.cmd_apdu_data_len - = (apdu.cmd_apdu_head[5] << 8) + apdu.cmd_apdu_head[6]; - if (icc_data_size == 7) - { /* No Lc but extended Le */ - apdu.expected_res_size = apdu.cmd_apdu_data_len; - apdu.cmd_apdu_data_len = 0; - } - else if (icc_data_size == apdu.cmd_apdu_data_len + 7) - apdu.expected_res_size = 0; - else if (icc_data_size == apdu.cmd_apdu_data_len + 7 + 2) - { - apdu.expected_res_size - = (icc_buffer[ICC_MSG_HEADER_SIZE + 7 - + apdu.cmd_apdu_data_len] << 8) - | icc_buffer[ICC_MSG_HEADER_SIZE + 7 - + apdu.cmd_apdu_data_len + 1]; - if (apdu.expected_res_size == 0) - apdu.expected_res_size = 65536; - } - else - { - icc_error (ICC_MSG_HEADER_SIZE + 5); - next_state = ICC_STATE_WAIT; - break; - } - } - else /* short Lc */ - { - apdu.cmd_apdu_data = apdu.cmd_apdu_head + 5; - apdu.cmd_apdu_data_len = apdu.cmd_apdu_head[4]; - if (icc_data_size == apdu.cmd_apdu_data_len + 5) - apdu.expected_res_size = 0; - else if (icc_data_size == apdu.cmd_apdu_data_len + 5 + 1) - { - apdu.expected_res_size - = icc_buffer[ICC_MSG_HEADER_SIZE + 5 - + apdu.cmd_apdu_data_len]; - if (apdu.expected_res_size == 0) - apdu.expected_res_size = 256; - } - else - { - icc_error (ICC_MSG_HEADER_SIZE + 5); - next_state = ICC_STATE_WAIT; - break; - } - } - - chEvtSignal (gpg_thread, EV_CMD_AVAILABLE); - next_state = ICC_STATE_EXECUTE; - } - else if (icc_header->param == 1) + if (c->icc_header.param == 0) { - icc_chain_p = icc_next_p; - icc_send_data_block (0, 0, 0x10); - next_state = ICC_STATE_RECEIVE; + DEBUG_INFO ("DUMP C\r\n"); + DEBUG_SHORT (c->icc_state); + DEBUG_BYTE (c->state); + DEBUG_WORD (c->p); + DEBUG_WORD (c->len); + DEBUG_BYTE (c->err); + DEBUG_WORD (c->epo->buf); + DEBUG_WORD (c->epo->cnt); + DEBUG_WORD (c->epo->buf_len); + DEBUG_WORD (c->a->cmd_apdu_data); + DEBUG_BINARY (c->a->cmd_apdu_data, c->a->cmd_apdu_data_len); + + if ((c->a->cmd_apdu_head[0] & 0x10) == 0) + { + if (c->state == APDU_STATE_COMMAND_CHAINING) + { /* command chaining finished */ + c->p += c->a->cmd_apdu_head[4]; + c->a->cmd_apdu_head[4] = 0; + c->a->cmd_apdu_data_len = c->p - c->a->cmd_apdu_data; + DEBUG_INFO ("CMD chaning finished.\r\n"); + } + + if (c->a->cmd_apdu_head[1] == INS_GET_RESPONSE + && c->state == APDU_STATE_RESULT_GET_RESPONSE) + { + size_t len = c->a->expected_res_size; + + if (c->len <= c->a->expected_res_size) + len = c->len; + + icc_send_data_block_gr (c, len); + if (c->len == 0) + c->state = APDU_STATE_RESULT; + c->icc_state = ICC_STATE_WAIT; + } + else + { /* Give this message to GPG thread */ + c->state = APDU_STATE_COMMAND_RECEIVED; + + c->a->sw = 0x9000; + c->a->res_apdu_data_len = 0; + c->a->res_apdu_data = &icc_buffer[5]; + + chEvtSignal (c->application, EV_CMD_AVAILABLE); + next_state = ICC_STATE_EXECUTE; + } + } + else /* command chaining is ongoing*/ + { + c->state = APDU_STATE_COMMAND_CHAINING; + c->p += c->a->cmd_apdu_head[4]; + c->len -= c->a->cmd_apdu_head[4]; + icc_send_data_block_0x9000 (c); + DEBUG_INFO ("CMD chaning...\r\n"); + } } else - { + { /* ICC block chaining is not supported. */ DEBUG_INFO ("ERR02\r\n"); - icc_error (ICC_OFFSET_PARAM); + icc_error (c, ICC_OFFSET_PARAM); } } - else if (icc_header->msg_type == ICC_SET_PARAMS - || icc_header->msg_type == ICC_GET_PARAMS) - icc_send_params (); - else if (icc_header->msg_type == ICC_SECURE) + else if (c->icc_header.msg_type == ICC_SET_PARAMS + || c->icc_header.msg_type == ICC_GET_PARAMS) + icc_send_params (c); + else if (c->icc_header.msg_type == ICC_SECURE) { if (icc_buffer[10] == 0x00) /* PIN verification */ { - icc_buffer[ICC_MSG_HEADER_SIZE+0] = icc_buffer[25]; - icc_buffer[ICC_MSG_HEADER_SIZE+1] = icc_buffer[26]; - icc_buffer[ICC_MSG_HEADER_SIZE+2] = icc_buffer[27]; - icc_buffer[ICC_MSG_HEADER_SIZE+3] = icc_buffer[28]; + icc_buffer[0] = icc_buffer[25-10]; + icc_buffer[1] = icc_buffer[26-10]; + icc_buffer[2] = icc_buffer[27-10]; + icc_buffer[3] = icc_buffer[28-10]; /**/ - icc_buffer[ICC_MSG_HEADER_SIZE+5] = 0; /* bConfirmPIN */ - icc_buffer[ICC_MSG_HEADER_SIZE+6] = icc_buffer[17]; /* bEntryValidationCondition */ - icc_buffer[ICC_MSG_HEADER_SIZE+7] = icc_buffer[18]; /* bNumberMessage */ - icc_buffer[ICC_MSG_HEADER_SIZE+8] = icc_buffer[19]; /* wLangId L */ - icc_buffer[ICC_MSG_HEADER_SIZE+9] = icc_buffer[20]; /* wLangId H */ - icc_buffer[ICC_MSG_HEADER_SIZE+10] = icc_buffer[21]; /* bMsgIndex */ + icc_buffer[5] = 0; /* bConfirmPIN */ + icc_buffer[6] = icc_buffer[17-10]; /* bEntryValidationCondition */ + icc_buffer[7] = icc_buffer[18-10]; /* bNumberMessage */ + icc_buffer[8] = icc_buffer[19-10]; /* wLangId L */ + icc_buffer[9] = icc_buffer[20-10]; /* wLangId H */ + icc_buffer[10] = icc_buffer[21-10]; /* bMsgIndex */ - apdu.seq = icc_seq; - apdu.cmd_apdu_head = &icc_buffer[ICC_MSG_HEADER_SIZE+0]; - apdu.cmd_apdu_data = &icc_buffer[ICC_MSG_HEADER_SIZE+5]; - apdu.cmd_apdu_data_len = 6; - apdu.expected_res_size = 0; + c->a->cmd_apdu_data_len = 6; + c->a->expected_res_size = 0; - apdu.sw = 0x9000; - apdu.res_apdu_data_len = 0; - apdu.res_apdu_data = &icc_buffer[ICC_MSG_HEADER_SIZE]; + c->a->sw = 0x9000; + c->a->res_apdu_data_len = 0; + c->a->res_apdu_data = &icc_buffer[5]; - chEvtSignal (gpg_thread, EV_VERIFY_CMD_AVAILABLE); + chEvtSignal (c->application, EV_VERIFY_CMD_AVAILABLE); next_state = ICC_STATE_EXECUTE; } else if (icc_buffer[10] == 0x01) /* PIN Modification */ { - uint8_t num_msgs = icc_buffer[21]; + uint8_t num_msgs = icc_buffer[21-10]; if (num_msgs == 0x00) num_msgs = 1; else if (num_msgs == 0xff) num_msgs = 3; - icc_buffer[ICC_MSG_HEADER_SIZE+0] = icc_buffer[27 + num_msgs]; - icc_buffer[ICC_MSG_HEADER_SIZE+1] = icc_buffer[28 + num_msgs]; - icc_buffer[ICC_MSG_HEADER_SIZE+2] = icc_buffer[29 + num_msgs]; - icc_buffer[ICC_MSG_HEADER_SIZE+3] = icc_buffer[30 + num_msgs]; + icc_buffer[0] = icc_buffer[27 + num_msgs-10]; + icc_buffer[1] = icc_buffer[28 + num_msgs-10]; + icc_buffer[2] = icc_buffer[29 + num_msgs-10]; + icc_buffer[3] = icc_buffer[30 + num_msgs-10]; /**/ - icc_buffer[ICC_MSG_HEADER_SIZE+5] = icc_buffer[19]; /* bConfirmPIN */ - icc_buffer[ICC_MSG_HEADER_SIZE+6] = icc_buffer[20]; /* bEntryValidationCondition */ - icc_buffer[ICC_MSG_HEADER_SIZE+7] = icc_buffer[21]; /* bNumberMessage */ - icc_buffer[ICC_MSG_HEADER_SIZE+8] = icc_buffer[22]; /* wLangId L */ - icc_buffer[ICC_MSG_HEADER_SIZE+9] = icc_buffer[23]; /* wLangId H */ - icc_buffer[ICC_MSG_HEADER_SIZE+10] = icc_buffer[24]; /* bMsgIndex, bMsgIndex1 */ + icc_buffer[5] = icc_buffer[19-10]; /* bConfirmPIN */ + icc_buffer[6] = icc_buffer[20-10]; /* bEntryValidationCondition */ + icc_buffer[7] = icc_buffer[21-10]; /* bNumberMessage */ + icc_buffer[8] = icc_buffer[22-10]; /* wLangId L */ + icc_buffer[9] = icc_buffer[23-10]; /* wLangId H */ + icc_buffer[10] = icc_buffer[24-10]; /* bMsgIndex, bMsgIndex1 */ if (num_msgs >= 2) - icc_buffer[ICC_MSG_HEADER_SIZE+11] = icc_buffer[25]; /* bMsgIndex2 */ + icc_buffer[11] = icc_buffer[25-10]; /* bMsgIndex2 */ if (num_msgs == 3) - icc_buffer[ICC_MSG_HEADER_SIZE+12] = icc_buffer[26]; /* bMsgIndex3 */ + icc_buffer[12] = icc_buffer[26-10]; /* bMsgIndex3 */ - apdu.seq = icc_seq; - apdu.cmd_apdu_head = &icc_buffer[ICC_MSG_HEADER_SIZE+0]; - apdu.cmd_apdu_data = &icc_buffer[ICC_MSG_HEADER_SIZE+5]; - apdu.cmd_apdu_data_len = 5 + num_msgs; - apdu.expected_res_size = 0; + c->a->cmd_apdu_data_len = 5 + num_msgs; + c->a->expected_res_size = 0; - apdu.sw = 0x9000; - apdu.res_apdu_data_len = 0; - apdu.res_apdu_data = &icc_buffer[ICC_MSG_HEADER_SIZE]; + c->a->sw = 0x9000; + c->a->res_apdu_data_len = 0; + c->a->res_apdu_data = &icc_buffer[5]; - chEvtSignal (gpg_thread, EV_MODIFY_CMD_AVAILABLE); + chEvtSignal (c->application, EV_MODIFY_CMD_AVAILABLE); next_state = ICC_STATE_EXECUTE; } else - icc_error (ICC_MSG_DATA_OFFSET); + icc_error (c, ICC_MSG_DATA_OFFSET); } else { DEBUG_INFO ("ERR03\r\n"); - DEBUG_BYTE (icc_header->msg_type); - icc_error (ICC_OFFSET_CMD_NOT_SUPPORTED); - } - break; - case ICC_STATE_RECEIVE: - if (icc_header->msg_type == ICC_POWER_OFF) - { - icc_chain_p = NULL; - next_state = icc_power_off (); - } - else if (icc_header->msg_type == ICC_SLOT_STATUS) - icc_send_status (); - else if (icc_header->msg_type == ICC_XFR_BLOCK) - { - if (icc_header->param == 2) /* Got the final block */ - { /* Give this message to GPG thread */ - int len = icc_next_p - icc_chain_p - ICC_MSG_HEADER_SIZE; - - memmove (icc_chain_p, icc_chain_p + ICC_MSG_HEADER_SIZE, len); - icc_next_p -= ICC_MSG_HEADER_SIZE; - icc_data_size = icc_next_p - icc_buffer - ICC_MSG_HEADER_SIZE; - icc_chain_p = NULL; - goto run_gpg_thread; - } - else /* icc_header->param == 3 is not supported. */ - { - DEBUG_INFO ("ERR08\r\n"); - icc_error (ICC_OFFSET_PARAM); - } - } - else - { - DEBUG_INFO ("ERR05\r\n"); - DEBUG_BYTE (icc_header->msg_type); - icc_chain_p = NULL; - icc_error (ICC_OFFSET_CMD_NOT_SUPPORTED); - next_state = ICC_STATE_WAIT; + DEBUG_BYTE (c->icc_header.msg_type); + icc_error (c, ICC_OFFSET_CMD_NOT_SUPPORTED); } break; case ICC_STATE_EXECUTE: - if (icc_header->msg_type == ICC_POWER_OFF) - next_state = icc_power_off (); - else if (icc_header->msg_type == ICC_SLOT_STATUS) - icc_send_status (); + if (c->icc_header.msg_type == ICC_POWER_OFF) + next_state = icc_power_off (c); + else if (c->icc_header.msg_type == ICC_SLOT_STATUS) + icc_send_status (c); else { DEBUG_INFO ("ERR04\r\n"); - DEBUG_BYTE (icc_header->msg_type); - icc_error (ICC_OFFSET_CMD_NOT_SUPPORTED); - } - break; - case ICC_STATE_SEND: - if (icc_header->msg_type == ICC_POWER_OFF) - next_state = icc_power_off (); - else if (icc_header->msg_type == ICC_SLOT_STATUS) - icc_send_status (); - else if (icc_header->msg_type == ICC_XFR_BLOCK) - { - if (icc_header->param == 0x10) - { - int len = apdu.res_apdu_data_len; - - if (apdu.res_apdu_data == NULL) - { /* send status word(s) of 0x9000 */ - len = 0; - - if (apdu.res_apdu_data_len == 2) - { - icc_buffer[ICC_MSG_HEADER_SIZE] = 0x90; - len++; - } - - icc_buffer[ICC_MSG_HEADER_SIZE+len] = 0x00; - len++; - } - else if (apdu.res_apdu_data != &icc_buffer[ICC_MSG_HEADER_SIZE]) - { - if (apdu.res_apdu_data_len > ICC_RESPONSE_MSG_DATA_SIZE) - { - memcpy (&icc_buffer[ICC_MSG_HEADER_SIZE], - apdu.res_apdu_data, ICC_RESPONSE_MSG_DATA_SIZE); - apdu.res_apdu_data += ICC_RESPONSE_MSG_DATA_SIZE; - apdu.res_apdu_data_len -= ICC_RESPONSE_MSG_DATA_SIZE; - } - else if (apdu.res_apdu_data_len == ICC_RESPONSE_MSG_DATA_SIZE) - { - memcpy (&icc_buffer[ICC_MSG_HEADER_SIZE], - apdu.res_apdu_data, ICC_RESPONSE_MSG_DATA_SIZE); - apdu.res_apdu_data = NULL; - apdu.res_apdu_data_len = 2; - } - else if (apdu.res_apdu_data_len - == ICC_RESPONSE_MSG_DATA_SIZE - 1) - { - memcpy (&icc_buffer[ICC_MSG_HEADER_SIZE], - apdu.res_apdu_data, apdu.res_apdu_data_len); - icc_buffer[ICC_MSG_HEADER_SIZE+apdu.res_apdu_data_len] - = 0x90; - apdu.res_apdu_data = NULL; - apdu.res_apdu_data_len = 1; - len += 1; - } - else if (apdu.res_apdu_data_len - <= ICC_RESPONSE_MSG_DATA_SIZE - 2) - { - memcpy (&icc_buffer[ICC_MSG_HEADER_SIZE], - apdu.res_apdu_data, apdu.res_apdu_data_len); - icc_buffer[ICC_MSG_HEADER_SIZE+apdu.res_apdu_data_len] - = 0x90; - icc_buffer[ICC_MSG_HEADER_SIZE+apdu.res_apdu_data_len+1] - = 0x00; - apdu.res_apdu_data = NULL; - apdu.res_apdu_data_len = 0; - len += 2; - } - } - else - { - memmove (&icc_buffer[ICC_MSG_HEADER_SIZE], - &icc_buffer[ICC_MSG_HEADER_SIZE] - +ICC_RESPONSE_MSG_DATA_SIZE, - apdu.res_apdu_data_len); - apdu.res_apdu_data_len -= ICC_RESPONSE_MSG_DATA_SIZE; - } - - if (len <= ICC_RESPONSE_MSG_DATA_SIZE) - { - icc_send_data_block (len, 0, 0x02); - next_state = ICC_STATE_WAIT; - } - else - icc_send_data_block (ICC_RESPONSE_MSG_DATA_SIZE, 0, 0x03); - } - else - { - DEBUG_INFO ("ERR0A\r\n"); - DEBUG_BYTE (icc_header->param >> 8); - DEBUG_BYTE (icc_header->param & 0xff); - icc_error (ICC_OFFSET_PARAM); - next_state = ICC_STATE_WAIT; - } - } - else - { - DEBUG_INFO ("ERR06\r\n"); - DEBUG_BYTE (icc_header->msg_type); - icc_error (ICC_OFFSET_CMD_NOT_SUPPORTED); - next_state = ICC_STATE_WAIT; + DEBUG_BYTE (c->icc_header.msg_type); + icc_error (c, ICC_OFFSET_CMD_NOT_SUPPORTED); } break; default: @@ -831,14 +1184,14 @@ icc_handle_data (void) } static enum icc_state -icc_handle_timeout (void) +icc_handle_timeout (struct ccid *c) { - enum icc_state next_state = icc_state; + enum icc_state next_state = c->icc_state; - switch (icc_state) + switch (c->icc_state) { case ICC_STATE_EXECUTE: - icc_send_data_block (0, ICC_CMD_STATUS_TIMEEXT, 0); + icc_send_data_block (c, ICC_CMD_STATUS_TIMEEXT); break; default: break; @@ -849,17 +1202,28 @@ icc_handle_timeout (void) #define USB_ICC_TIMEOUT MS2ST(1950) + +static struct ccid ccid; + + msg_t USBthread (void *arg) { + struct ep_in *epi = &endpoint_in; + struct ep_out *epo = &endpoint_out; + struct ccid *c = &ccid; + struct apdu *a = &apdu; + (void)arg; - icc_thread = chThdSelf (); + epi_init (epi, ENDP1, notify_tx, c); + epo_init (epo, ENDP2, notify_icc, c); + ccid_init (c, epi, epo, a, chThdSelf ()); + apdu_init (a); + chEvtClear (ALL_EVENTS); - icc_state = ICC_STATE_START; - - icc_prepare_receive (0); + icc_prepare_receive (c); while (1) { eventmask_t m; @@ -867,52 +1231,52 @@ USBthread (void *arg) m = chEvtWaitOneTimeout (ALL_EVENTS, USB_ICC_TIMEOUT); if (m == EV_RX_DATA_READY) - icc_state = icc_handle_data (); + c->icc_state = icc_handle_data (c); else if (m == EV_EXEC_FINISHED) - if (icc_state == ICC_STATE_EXECUTE) + if (c->icc_state == ICC_STATE_EXECUTE) { - if (apdu.res_apdu_data != &icc_buffer[ICC_MSG_HEADER_SIZE]) - { - /* Assume that - * apdu.res_apdu_data_len > ICC_RESPONSE_MSG_DATA_SIZE - */ - memcpy (&icc_buffer[ICC_MSG_HEADER_SIZE], - apdu.res_apdu_data, ICC_RESPONSE_MSG_DATA_SIZE); - apdu.res_apdu_data += ICC_RESPONSE_MSG_DATA_SIZE; - } - else - { - icc_buffer[ICC_MSG_HEADER_SIZE+apdu.res_apdu_data_len] = (apdu.sw >> 8); - icc_buffer[ICC_MSG_HEADER_SIZE+apdu.res_apdu_data_len+1] = (apdu.sw & 0xff); - apdu.res_apdu_data_len += 2; - } + c->a->cmd_apdu_data_len = 0; + c->sw1sw2[0] = c->a->sw >> 8; + c->sw1sw2[1] = c->a->sw & 0xff; - if (apdu.res_apdu_data_len <= ICC_RESPONSE_MSG_DATA_SIZE) + if (c->a->res_apdu_data_len <= c->a->expected_res_size) { - icc_send_data_block (apdu.res_apdu_data_len, 0, 0); - icc_state = ICC_STATE_WAIT; + c->state = APDU_STATE_RESULT; + icc_send_data_block (c, 0); + c->icc_state = ICC_STATE_WAIT; } else { - icc_send_data_block (ICC_RESPONSE_MSG_DATA_SIZE, 0, 0x01); - apdu.res_apdu_data_len -= ICC_RESPONSE_MSG_DATA_SIZE; - icc_state = ICC_STATE_SEND; + c->state = APDU_STATE_RESULT_GET_RESPONSE; + c->p = c->a->res_apdu_data; + c->len = c->a->res_apdu_data_len; + icc_send_data_block_gr (c, c->a->expected_res_size); + c->icc_state = ICC_STATE_WAIT; } } else - { /* error */ + { DEBUG_INFO ("ERR07\r\n"); } else if (m == EV_TX_FINISHED) { - if (icc_state == ICC_STATE_START || icc_state == ICC_STATE_WAIT - || icc_state == ICC_STATE_SEND) - icc_prepare_receive (0); - else if (icc_state == ICC_STATE_RECEIVE) - icc_prepare_receive (1); + if (c->state == APDU_STATE_RESULT) + { + c->state = APDU_STATE_WAIT_COMMAND; + c->p = c->a->cmd_apdu_data; + c->len = MAX_CMD_APDU_DATA_SIZE; + c->err = 0; + c->a->cmd_apdu_data_len = 0; + c->a->expected_res_size = 0; + } + + if (c->state == APDU_STATE_WAIT_COMMAND + || c->state == APDU_STATE_COMMAND_CHAINING + || c->state == APDU_STATE_RESULT_GET_RESPONSE) + icc_prepare_receive (c); } else /* Timeout */ - icc_state = icc_handle_timeout (); + c->icc_state = icc_handle_timeout (c); } return 0; diff --git a/src/usb_desc.c b/src/usb_desc.c index 43c09eb..fdb2f37 100644 --- a/src/usb_desc.c +++ b/src/usb_desc.c @@ -102,7 +102,7 @@ static const uint8_t gnukConfigDescriptor[] = { * ccid-driver. */ 0x42, 0x08, 0x04, 0x00, /* dwFeatures (not ICCD): - * Short and extended APDU level: 0x40000 * + * Short APDU level : 0x20000 * * (what? means ICCD?) : 0x00800 * * Automatic IFSD : 0x00400 * NAD value other than 0x00 : 0x00200 @@ -115,7 +115,7 @@ static const uint8_t gnukConfigDescriptor[] = { * Auto activaction of ICC : 0x00004 * Automatic conf. based on ATR : 0x00002 g */ - 0x40, 0x01, 0, 0, /* dwMaxCCIDMessageLength */ + 0x0f, 0x01, 0, 0, /* dwMaxCCIDMessageLength: 271 */ 0xff, /* bClassGetResponse: */ 0xff, /* bClassEnvelope: */ 0, 0, /* wLCDLayout: FIXED VALUE */ diff --git a/src/usb_lld.c b/src/usb_lld.c index 97c9f8d..4af51fe 100644 --- a/src/usb_lld.c +++ b/src/usb_lld.c @@ -27,3 +27,76 @@ void usb_lld_init (void) { RCC->APB1RSTR = RCC_APB1RSTR_USBRST; RCC->APB1RSTR = 0; } + +void usb_lld_to_pmabuf (const void *src, uint16_t wPMABufAddr, size_t n) +{ + const uint8_t *s = (const uint8_t *)src; + uint16_t *p; + uint16_t w; + + if (n == 0) + return; + + if ((wPMABufAddr & 1)) + { + p = (uint16_t *)(PMAAddr + (wPMABufAddr - 1) * 2); + w = *p; + w = (w & 0xff) | (*s++) << 8; + *p = w; + p += 2; + n--; + } + else + p = (uint16_t *)(PMAAddr + wPMABufAddr * 2); + + while (n >= 2) + { + w = *s++; + w |= (*s++) << 8; + *p = w; + p += 2; + n -= 2; + } + + if (n > 0) + { + w = *s; + *p = w; + } +} + +void usb_lld_from_pmabuf (void *dst, uint16_t wPMABufAddr, size_t n) +{ + uint8_t *d = (uint8_t *)dst; + uint16_t *p; + uint16_t w; + + if (n == 0) + return; + + if ((wPMABufAddr & 1)) + { + p = (uint16_t *)(PMAAddr + (wPMABufAddr - 1) * 2); + w = *p; + *d++ = (w >> 8); + p += 2; + n--; + } + else + p = (uint16_t *)(PMAAddr + wPMABufAddr * 2); + + while (n >= 2) + { + w = *p; + *d++ = (w & 0xff); + *d++ = (w >> 8); + p += 2; + n -= 2; + } + + if (n > 0) + { + w = *p; + *d = (w & 0xff); + } +} diff --git a/src/usb_lld.h b/src/usb_lld.h index 0ec00d3..a74dcd7 100644 --- a/src/usb_lld.h +++ b/src/usb_lld.h @@ -1,6 +1,9 @@ #define STM32_USB_IRQ_PRIORITY 11 void usb_lld_init (void); +extern void usb_lld_to_pmabuf (const void *src, uint16_t addr, size_t n); +extern void usb_lld_from_pmabuf (void *dst, uint16_t addr, size_t n); + extern inline void usb_lld_stall_tx (int ep_num) { SetEPTxStatus (ep_num, EP_TX_STALL); @@ -16,10 +19,10 @@ extern inline int usb_lld_tx_data_len (int ep_num) return GetEPTxCount (ep_num); } -extern inline void usb_lld_txcpy (const uint8_t *src, +extern inline void usb_lld_txcpy (const void *src, int ep_num, int offset, size_t len) { - UserToPMABufferCopy ((uint8_t *)src, GetEPTxAddr (ep_num) + offset, len); + usb_lld_to_pmabuf (src, GetEPTxAddr (ep_num) + offset, len); } extern inline void usb_lld_tx_enable (int ep_num, size_t len) @@ -28,9 +31,9 @@ extern inline void usb_lld_tx_enable (int ep_num, size_t len) SetEPTxValid (ep_num); } -extern inline void usb_lld_write (uint8_t ep_num, void *buf, size_t len) +extern inline void usb_lld_write (uint8_t ep_num, const void *buf, size_t len) { - UserToPMABufferCopy (buf, GetEPTxAddr (ep_num), len); + usb_lld_to_pmabuf (buf, GetEPTxAddr (ep_num), len); SetEPTxCount (ep_num, len); SetEPTxValid (ep_num); } @@ -48,5 +51,5 @@ extern inline int usb_lld_rx_data_len (int ep_num) extern inline void usb_lld_rxcpy (uint8_t *dst, int ep_num, int offset, size_t len) { - PMAToUserBufferCopy (dst, GetEPRxAddr (ep_num) + offset, len); + usb_lld_from_pmabuf (dst, GetEPRxAddr (ep_num) + offset, len); } diff --git a/tool/gnuk_put_binary.py b/tool/gnuk_put_binary.py index d3de957..0b81194 100755 --- a/tool/gnuk_put_binary.py +++ b/tool/gnuk_put_binary.py @@ -44,7 +44,7 @@ class GnukToken(object): self.connection = cardservice.connection def cmd_verify(self, who, passwd): - apdu = [0x00, 0x20, 0x00, 0x80+who, 0, 0, len(passwd)] + s2l(passwd) + apdu = [0x00, 0x20, 0x00, 0x80+who, len(passwd)] + s2l(passwd) response, sw1, sw2 = self.connection.transmit(apdu) if not (sw1 == 0x90 and sw2 == 0x00): raise ValueError, ("%02x%02x" % (sw1, sw2)) @@ -59,10 +59,20 @@ class GnukToken(object): while count*256 < data_len: if count == 0: d = data[:256] - apdu = [0x00, ins, 0x80+fileid, 0x00, 0, len(d)>>8, len(d)&0xff] + s2l(d) + if len(d) <= 255: + apdu = [0x00, ins, 0x80+fileid, 0x00, len(d) ] + s2l(d) + else: + apdu0 = [0x10, ins, 0x80+fileid, 0x00, 255 ] + s2l(d[:255]) + response, sw1, sw2 = self.connection.transmit(apdu0) + apdu = [0x00, ins, 0x80+fileid, 0x00, 1 ] + s2l(d[255:]) else: d = data[256*count:256*(count+1)] - apdu = [0x00, ins, count, 0x00, 0, len(d)>>8, len(d)&0xff] + s2l(d) + if len(d) <= 255: + apdu = [0x00, ins, count, 0x00, len(d) ] + s2l(d) + else: + apdu0 = [0x10, ins, count, 0x00, 255 ] + s2l(d[:255]) + response, sw1, sw2 = self.connection.transmit(apdu0) + apdu = [0x00, ins, 0x80+fileid, 0x00, 1 ] + s2l(d[255:]) response, sw1, sw2 = self.connection.transmit(apdu) if not (sw1 == 0x90 and sw2 == 0x00): if is_update: @@ -72,13 +82,13 @@ class GnukToken(object): count += 1 def cmd_select_openpgp(self): - apdu = [0x00, 0xa4, 0x04, 0x00, 6, 0xd2, 0x76, 0x00, 0x01, 0x24, 0x01 ] + apdu = [0x00, 0xa4, 0x04, 0x0c, 6, 0xd2, 0x76, 0x00, 0x01, 0x24, 0x01 ] response, sw1, sw2 = self.connection.transmit(apdu) if not (sw1 == 0x90 and sw2 == 0x00): raise ValueError, ("%02x%02x" % (sw1, sw2)) def cmd_get_data(self, tagh, tagl): - apdu = [0x00, 0xca, tagh, tagl] + apdu = [0x00, 0xca, tagh, tagl ] response, sw1, sw2 = self.connection.transmit(apdu) if not (sw1 == 0x90 and sw2 == 0x00): raise ValueError, ("%02x%02x" % (sw1, sw2)) @@ -146,18 +156,6 @@ if __name__ == '__main__': exit(1) print "Writing serial number" data = binascii.unhexlify(serial_data_hex) - elif sys.argv[1] == '-r': - fileid = 1 # Random number bits - if len(sys.argv) == 3: - filename = sys.argv[2] - f = open(filename) - else: - filename = stdin - f = sys.stdin - data = f.read() - f.close() - print "%s: %d" % (filename, len(data)) - print "Updating random bits" else: fileid = 0 # Card holder certificate filename = sys.argv[1]