summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Levy <alevy@redhat.com>2010-10-17 13:10:32 +0200
committerAlon Levy <alevy@redhat.com>2010-11-25 16:49:50 +0200
commit58120639e969d02510e8f2c1c4ef4c1fb2ffb19a (patch)
tree2a25ce7649815178f17d27e93051959baf08a0c2
parente0ffeba7d37013b27f828a1559ed80ac89320093 (diff)
ccid: add passthru card device
For the client side utility vscclient, see libcac_card: libcac_card http://cgit.freedesktop.org/~alon/cac_card (temporary home) written by Robert Relyea <rrelyea@redhat.com> Signed-off-by: Alon Levy <alevy@redhat.com>
-rw-r--r--Makefile.objs2
-rw-r--r--hw/ccid-card-passthru.c277
-rw-r--r--hw/vscard_common.h130
3 files changed, 408 insertions, 1 deletions
diff --git a/Makefile.objs b/Makefile.objs
index 713131f49..2059e89bd 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -183,7 +183,7 @@ hw-obj-$(CONFIG_FDC) += fdc.o
hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
hw-obj-$(CONFIG_DMA) += dma.o
-hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o
+hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
# PPC devices
hw-obj-$(CONFIG_OPENPIC) += openpic.o
diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
new file mode 100644
index 000000000..fbc41240c
--- /dev/null
+++ b/hw/ccid-card-passthru.c
@@ -0,0 +1,277 @@
+/*
+ * CCID Card Device emulation
+ *
+ * Copyright (c) 2010 Red Hat.
+ * Written by Alon Levy.
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "qemu-char.h"
+#include "monitor.h"
+#include "hw/ccid.h"
+#include "hw/vscard_common.h"
+
+#define DPRINTF(card, lvl, fmt, ...) \
+do { if (lvl <= card->debug) { printf("ccid-card: " fmt , ## __VA_ARGS__); } } while (0)
+
+/* Passthru card */
+
+
+// TODO: do we still need this?
+uint8_t DEFAULT_ATR[] = {
+/* From some example somewhere
+ 0x3B, 0xB0, 0x18, 0x00, 0xD1, 0x81, 0x05, 0xB1, 0x40, 0x38, 0x1F, 0x03, 0x28
+ */
+
+/* From an Athena smart card */
+ 0x3B, 0xD5, 0x18, 0xFF, 0x80, 0x91, 0xFE, 0x1F, 0xC3, 0x80, 0x73, 0xC8, 0x21, 0x13, 0x08
+
+}; /* maximum size of ATR - from 7816-3 */
+
+
+#define PASSTHRU_DEV_NAME "ccid-card-passthru"
+#define VSCARD_IN_SIZE 65536
+#define MAX_ATR_SIZE 40
+
+typedef struct PassthruState PassthruState;
+
+struct PassthruState {
+ CCIDCardState base;
+ CharDriverState *cs;
+ uint8_t vscard_in_data[VSCARD_IN_SIZE];
+ uint32_t vscard_in_pos;
+ uint32_t vscard_in_hdr;
+ uint8_t atr[MAX_ATR_SIZE];
+ uint8_t atr_length;
+ uint8_t debug;
+};
+
+/* VSCard protocol over chardev
+ * This code should not depend on the card type.
+ * */
+
+static void ccid_card_vscard_send_msg(
+ PassthruState *s, VSCMsgType type, reader_id_t reader_id,
+ const uint8_t* payload, uint32_t length)
+{
+ VSCMsgHeader scr_msg_header;
+
+ scr_msg_header.type = type;
+ scr_msg_header.reader_id = reader_id;
+ scr_msg_header.length = length;
+ qemu_chr_write(s->cs, (uint8_t*)&scr_msg_header, sizeof(VSCMsgHeader));
+ qemu_chr_write(s->cs, payload, length);
+}
+
+static void ccid_card_vscard_send_apdu(
+ PassthruState *s, const uint8_t* apdu, uint32_t length)
+{
+ ccid_card_vscard_send_msg(s, VSC_APDU, VSCARD_MINIMAL_READER_ID, apdu, length);
+}
+
+static void ccid_card_vscard_send_error(
+ PassthruState *s, reader_id_t reader_id, VSCErrorCode code)
+{
+ VSCMsgError msg = {.code=code};
+
+ ccid_card_vscard_send_msg(s, VSC_Error, reader_id, (uint8_t*)&msg, sizeof(msg));
+}
+
+static void ccid_card_vscard_send_init(PassthruState *s)
+{
+ VSCMsgInit msg = {.version=VSCARD_VERSION};
+
+ ccid_card_vscard_send_msg(s, VSC_Init, VSCARD_UNDEFINED_READER_ID,
+ (uint8_t*)&msg, sizeof(msg));
+}
+
+static int ccid_card_vscard_can_read(void *opaque)
+{
+ return 65535;
+}
+
+static void ccid_card_vscard_handle_message(PassthruState *card,
+ VSCMsgHeader* scr_msg_header)
+{
+ uint8_t *data = (uint8_t*)&scr_msg_header[1];
+
+ switch (scr_msg_header->type) {
+ case VSC_ATR:
+ DPRINTF(card, 1, "VSC_ATR %d\n", scr_msg_header->length);
+ assert(scr_msg_header->length <= MAX_ATR_SIZE);
+ memcpy(card->atr, data, scr_msg_header->length);
+ card->atr_length = scr_msg_header->length;
+ ccid_card_card_inserted(&card->base);
+ break;
+ case VSC_APDU:
+ ccid_card_send_apdu_to_guest(&card->base, data, scr_msg_header->length);
+ break;
+ case VSC_CardRemove:
+ DPRINTF(card, 1, "VSC_CardRemove\n");
+ ccid_card_card_removed(&card->base);
+ break;
+ case VSC_Init:
+ break;
+ case VSC_Error:
+ ccid_card_card_error(&card->base, *(uint64_t*)data);
+ break;
+ case VSC_ReaderAdd:
+ if (ccid_card_ccid_attach(&card->base) < 0) {
+ ccid_card_vscard_send_error(card, VSCARD_UNDEFINED_READER_ID,
+ VSC_CANNOT_ADD_MORE_READERS);
+ } else {
+ ccid_card_vscard_send_msg(card, VSC_ReaderAddResponse,
+ VSCARD_MINIMAL_READER_ID, NULL, 0);
+ }
+ break;
+ case VSC_ReaderRemove:
+ ccid_card_ccid_detach(&card->base);
+ break;
+ default:
+ printf("usb-ccid: chardev: unexpected message of type %X\n",
+ scr_msg_header->type);
+ ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
+ VSC_GENERAL_ERROR);
+ }
+}
+
+static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size)
+{
+ PassthruState *card = opaque;
+ VSCMsgHeader *hdr;
+
+ assert(card->vscard_in_pos + size <= VSCARD_IN_SIZE);
+ memcpy(card->vscard_in_data + card->vscard_in_pos, buf, size);
+ card->vscard_in_pos += size;
+ hdr = (VSCMsgHeader*)(card->vscard_in_data + card->vscard_in_hdr);
+
+ while ((card->vscard_in_pos - card->vscard_in_hdr >= sizeof(VSCMsgHeader)) &&
+ (card->vscard_in_pos - card->vscard_in_hdr - sizeof(VSCMsgHeader) >=
+ hdr->length)) {
+ ccid_card_vscard_handle_message(card, hdr);
+ card->vscard_in_hdr += hdr->length + sizeof(VSCMsgHeader);
+ hdr = (VSCMsgHeader*)(card->vscard_in_data + card->vscard_in_hdr);
+ }
+ if (card->vscard_in_hdr == card->vscard_in_pos) {
+ card->vscard_in_pos = card->vscard_in_hdr = 0;
+ }
+}
+
+static void ccid_card_vscard_event(void *opaque, int event)
+{
+ PassthruState *card = opaque;
+
+ switch (event) {
+ case CHR_EVENT_BREAK:
+ break;
+ case CHR_EVENT_FOCUS:
+ break;
+ case CHR_EVENT_OPENED:
+ DPRINTF(card, 1, "%s: CHR_EVENT_OPENED\n", __func__);
+ break;
+ }
+}
+
+/* End VSCard handling */
+
+static void passthru_apdu_from_guest(CCIDCardState *base, const uint8_t *apdu, uint32_t len)
+{
+ PassthruState *card = DO_UPCAST(PassthruState, base, base);
+
+ if (!card->cs) {
+ printf("ccid-passthru: no chardev, discarding apdu length %d\n", len);
+ return;
+ }
+ ccid_card_vscard_send_apdu(card, apdu, len);
+}
+
+static const uint8_t* passthru_get_atr(CCIDCardState *base, uint32_t *len)
+{
+ PassthruState *card = DO_UPCAST(PassthruState, base, base);
+
+ *len = card->atr_length;
+ return card->atr;
+}
+
+static int passthru_initfn(CCIDCardState *base)
+{
+ PassthruState *card = DO_UPCAST(PassthruState, base, base);
+
+ card->vscard_in_pos = 0;
+ card->vscard_in_hdr = 0;
+ if (card->cs) {
+ DPRINTF(card, 1, "initing chardev\n");
+ qemu_chr_add_handlers(card->cs,
+ ccid_card_vscard_can_read,
+ ccid_card_vscard_read,
+ ccid_card_vscard_event, card);
+ ccid_card_vscard_send_init(card);
+ }
+ assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE);
+ memcpy(card->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR));
+ card->atr_length = sizeof(DEFAULT_ATR);
+ return 0;
+}
+
+static int passthru_exitfn(CCIDCardState *base)
+{
+ PassthruState *card = DO_UPCAST(PassthruState, base, base);
+
+ if (card->cs) {
+ qemu_chr_close(card->cs);
+ }
+ return 0;
+}
+
+static void passthru_pre_save(void *opaque)
+{
+ PassthruState *card = opaque;
+ VSCMsgReconnect reconnect;
+
+ reconnect.ip = 0; // TODO - does the bus keep the target ip? s->migration_target_ip;
+ reconnect.port = 0; // TODO - does the bus keep the target ip? s->migration_target_port;
+ ccid_card_vscard_send_msg(card, VSC_Reconnect, VSCARD_UNDEFINED_READER_ID,
+ (uint8_t*)&reconnect, sizeof(reconnect));
+}
+
+static VMStateDescription passthru_vmstate = {
+ .name = PASSTHRU_DEV_NAME,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_save = passthru_pre_save,
+ .fields = (VMStateField []) {
+ VMSTATE_BUFFER(vscard_in_data, PassthruState),
+ VMSTATE_UINT32(vscard_in_pos, PassthruState),
+ VMSTATE_UINT32(vscard_in_hdr, PassthruState),
+ VMSTATE_BUFFER(atr, PassthruState),
+ VMSTATE_UINT8(atr_length, PassthruState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static CCIDCardInfo passthru_card_info = {
+ .qdev.name = PASSTHRU_DEV_NAME,
+ .qdev.size = sizeof(PassthruState),
+ .qdev.vmsd = &passthru_vmstate,
+ .initfn = passthru_initfn,
+ .exitfn = passthru_exitfn,
+ .get_atr = passthru_get_atr,
+ .apdu_from_guest = passthru_apdu_from_guest,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", PassthruState, cs),
+ DEFINE_PROP_UINT8("debug", PassthruState, debug, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void ccid_card_passthru_register_devices(void)
+{
+ ccid_card_qdev_register(&passthru_card_info);
+ // TODO: passthru local card (or: just a case of passthru with no chardev
+ // given and instead some other arguments that would be required for local
+ // card anyway and can be shared with the emulated local card)
+ // TODO: emulated local card
+}
+
+device_init(ccid_card_passthru_register_devices)
diff --git a/hw/vscard_common.h b/hw/vscard_common.h
new file mode 100644
index 000000000..0468ea3b4
--- /dev/null
+++ b/hw/vscard_common.h
@@ -0,0 +1,130 @@
+/* Virtual Smart Card protocol definition
+ *
+ * This protocol is between a host implementing a group of virtual smart card
+ * reader, and a client implementing a virtual smart card, or passthrough to
+ * a real card.
+ *
+ * The current implementation passes the raw APDU's from 7816 and additionally
+ * contains messages to setup and teardown readers, handle insertion and
+ * removal of cards, negotiate the protocol and provide for error responses.
+ *
+ * Copyright (c) 2010 Red Hat.
+ *
+ * This code is licensed under the LGPL.
+ */
+
+#ifndef _VSCARD_COMMON_H
+#define _VSCARD_COMMON_H
+
+#include <stdint.h>
+
+#define VERSION_MAJOR_BITS 11
+#define VERSION_MIDDLE_BITS 11
+#define VERSION_MINOR_BITS 10
+
+#define MAKE_VERSION(major, middle, minor) \
+ ( (major << (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \
+ | (middle << VERSION_MINOR_BITS) \
+ | (minor) )
+
+/** IMPORTANT NOTE on VERSION
+ *
+ * The version below MUST be changed whenever a change in this file is made.
+ *
+ * The last digit, the minor, is for bug fix changes only.
+ *
+ * The middle digit is for backward / forward compatible changes, updates
+ * to the existing messages, addition of fields.
+ *
+ * The major digit is for a breaking change of protocol, presumably
+ * something that cannot be accomodated with the existing protocol.
+ */
+
+#define VSCARD_VERSION MAKE_VERSION(0,0,1)
+
+typedef enum {
+ VSC_Init,
+ VSC_Error,
+ VSC_ReaderAdd,
+ VSC_ReaderAddResponse,
+ VSC_ReaderRemove,
+ VSC_ATR,
+ VSC_CardRemove,
+ VSC_APDU,
+ VSC_Reconnect
+} VSCMsgType;
+
+typedef enum {
+ VSC_GENERAL_ERROR=1,
+ VSC_CANNOT_ADD_MORE_READERS,
+} VSCErrorCode;
+
+typedef uint32_t reader_id_t;
+#define VSCARD_UNDEFINED_READER_ID 0xfffffffe
+#define VSCARD_MINIMAL_READER_ID 0
+
+typedef struct VSCMsgHeader {
+ VSCMsgType type;
+ reader_id_t reader_id;
+ uint32_t length;
+ uint8_t data[0];
+} VSCMsgHeader;
+
+/* VSCMsgInit Client <-> Host
+ * Host replies with allocated reader id in ReaderAddResponse
+ * */
+typedef struct VSCMsgInit {
+ uint32_t version;
+} VSCMsgInit;
+
+/* VSCMsgError Client <-> Host
+ * */
+typedef struct VSCMsgError {
+ uint32_t code;
+} VSCMsgError;
+
+/* VSCMsgReaderAdd Client -> Host
+ * Host replies with allocated reader id in ReaderAddResponse
+ * name - name of the reader on client side.
+ * */
+typedef struct VSCMsgReaderAdd {
+ uint8_t name[0];
+} VSCMsgReaderAdd;
+
+/* VSCMsgReaderAddResponse Host -> Client
+ * Reply to ReaderAdd
+ * */
+typedef struct VSCMsgReaderAddResponse {
+} VSCMsgReaderAddResponse;
+
+/* VSCMsgReaderRemove Client -> Host
+ * */
+typedef struct VSCMsgReaderRemove {
+} VSCMsgReaderRemove;
+
+/* VSCMsgATR Client -> Host
+ * Answer to reset. Sent for card insertion or card reset.
+ * */
+typedef struct VSCMsgATR {
+ uint8_t atr[0];
+} VSCMsgATR;
+
+/* VSCMsgCardRemove Client -> Host
+ * */
+typedef struct VSCMsgCardRemove {
+} VSCMsgCardRemove;
+
+/* VSCMsgAPDU Client <-> Host
+ * */
+typedef struct VSCMsgAPDU {
+ uint8_t data[0];
+} VSCMsgAPDU;
+
+/* VSCMsgReconnect Host -> Client
+ * */
+typedef struct VSCMsgReconnect {
+ uint32_t ip;
+ uint16_t port;
+} VSCMsgReconnect;
+
+#endif