diff --git a/ccid/README b/ccid/README index 02ffd71..7b9e18d 100644 --- a/ccid/README +++ b/ccid/README @@ -23,6 +23,32 @@ INSTALLATION See file INSTALL. +HINTS ON GADGETFS +----------------- + +To create an USB Gadget in both USB host and USB client mode, you need to load +the kernel module gadgetfs. A guide focused on Debian based systems to run and +compile gadgetfs, you can find here: +http://wiki.openmoko.org/wiki/Building_Gadget_USB_Module + +On OpenMoko it is likely, that you need to patch your kernel (see +http://docs.openmoko.org/trac/ticket/2206). If you also want to switch multiple +times between gadgetfs and g_ether an other patch is needed (see +https://docs.openmoko.org/trac/ticket/2240). + + +USAGE +----- +When testing PACE with either PIN, CAN, MRZ or PUK, the program will be in +interactive mode. Here you can enter APDUs which are to be converted according +to the secure messaging parameter and to be sent to the card. Herefor insert +the APDU in hex with a colon to separate the bytes or without it. So to read +the current selected file both of these forms would be valid: +00:b0:02:00:00 +00B0020000 + + + QUESTIONS --------- diff --git a/ccid/ccid.c b/ccid/ccid.c index 4654736..18dae11 100644 --- a/ccid/ccid.c +++ b/ccid/ccid.c @@ -171,7 +171,7 @@ void ccid_shutdown() sc_release_context(ctx); } -static int build_apdu(const __u8 *buf, size_t len, sc_apdu_t *apdu) +int build_apdu(const __u8 *buf, size_t len, sc_apdu_t *apdu) { const __u8 *p; size_t len0; @@ -230,7 +230,7 @@ static int build_apdu(const __u8 *buf, size_t len, sc_apdu_t *apdu) apdu->flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL; - sc_error(ctx, "APDU, %d byte(s):\tins=%02x p1=%02x p2=%02x", + sc_debug(ctx, "APDU, %d bytes:\tins=%02x p1=%02x p2=%02x", (unsigned int) len0, apdu->ins, apdu->p1, apdu->p2); SC_FUNC_RETURN(ctx, SC_LOG_TYPE_VERBOSE, SC_SUCCESS); @@ -269,8 +269,9 @@ static int get_rapdu(sc_apdu_t *apdu, size_t slot, __u8 **buf, size_t *resplen) *buf = apdu->resp; *resplen = apdu->resplen + sizeof(__u8) + sizeof(__u8); - sc_debug(ctx, "R-APDU, %d byte(s):\tsw1=%02x sw2=%02x", - (unsigned int) *resplen, apdu->sw1, apdu->sw2); + sc_debug(ctx, "R-APDU, %d byte%s:\tsw1=%02x sw2=%02x", + (unsigned int) *resplen, !*resplen ? "" : "s", + apdu->sw1, apdu->sw2); SC_FUNC_RETURN(ctx, SC_LOG_TYPE_VERBOSE, SC_SUCCESS); @@ -1296,19 +1297,19 @@ int ccid_state_changed(RDR_to_PC_NotifySlotChange_t **slotchange, int timeout) return 0; } -int ccid_testpace() +int ccid_testpace(u8 pin_id, const char *pin, size_t pinlen) { int i; for (i = 0; i < sizeof *card_in_slot; i++) { - if (!card_in_slot[i] - && (sc_detect_card_presence(reader, 0) & SC_SLOT_CARD_PRESENT)) { - sc_connect_card(reader, i, &card_in_slot[i]); - } - - if (sc_card_valid(card_in_slot[i])) { - return pace_test(card_in_slot[i]); + if (sc_detect_card_presence(reader, 0) & SC_SLOT_CARD_PRESENT) { + if (!card_in_slot[i] || !sc_card_valid(card_in_slot[i])) { + sc_connect_card(reader, i, &card_in_slot[i]); + } + return pace_test(card_in_slot[i], pin_id, pin, pinlen); } } + sc_error(ctx, "No card found."); + return SC_ERROR_SLOT_NOT_FOUND; } diff --git a/ccid/ccid.h b/ccid/ccid.h index 1700bcf..e41a21d 100644 --- a/ccid/ccid.h +++ b/ccid/ccid.h @@ -20,6 +20,7 @@ #define _CCID_H #include +#include #ifdef __cplusplus extern "C" { @@ -270,7 +271,9 @@ void ccid_shutdown(); int ccid_parse_bulkin(const __u8* inbuf, __u8** outbuf); int ccid_parse_control(struct usb_ctrlrequest *setup, __u8 **outbuf); int ccid_state_changed(RDR_to_PC_NotifySlotChange_t **slotchange, int timeout); -int ccid_testpace(); +int ccid_testpace(u8 pin_id, const char *pin, size_t pinlen); + +int build_apdu(const __u8 *buf, size_t len, sc_apdu_t *apdu); #ifdef __cplusplus } diff --git a/ccid/pace.c b/ccid/pace.c index 931f449..ed3f9a5 100644 --- a/ccid/pace.c +++ b/ccid/pace.c @@ -18,6 +18,8 @@ */ #include "pace.h" #include "sm.h" +#include "ccid.h" +#include #include #include #include @@ -139,14 +141,18 @@ IMPLEMENT_ASN1_FUNCTIONS(PACE_GEN_AUTH_R) #ifdef NO_PACE inline int GetReadersPACECapabilities(sc_card_t *card, - const __u8 *in, __u8 **out, size_t *outlen) { + const __u8 *in, __u8 **out, size_t *outlen) +{ SC_FUNC_RETURN(card->ctx, SC_LOG_TYPE_ERROR, SC_ERROR_NOT_SUPPORTED); } inline int EstablishPACEChannel(sc_card_t *card, const __u8 *in, - __u8 **out, size_t *outlen, struct sm_ctx *sm_ctx) { + __u8 **out, size_t *outlen, struct sm_ctx *sm_ctx) +{ SC_FUNC_RETURN(card->ctx, SC_LOG_TYPE_ERROR, SC_ERROR_NOT_SUPPORTED); } -int pace_test(sc_card_t *card) { +int pace_test(sc_card_t *card, + enum s_type pin_id, const char *pin, size_t pinlen) +{ SC_FUNC_RETURN(card->ctx, SC_LOG_TYPE_ERROR, SC_ERROR_NOT_SUPPORTED); } int pace_sm_encrypt(sc_card_t *card, struct *sm_ctx, @@ -567,9 +573,10 @@ static PACE_SEC * get_psec(sc_card_t *card, const char *pin, size_t length_pin, u8 pin_id) { sc_ui_hints_t hints; - char *p; + char *p = NULL; PACE_SEC *r; int sc_result; + size_t len; if (pin && length_pin) return PACE_SEC_new(pin, length_pin, pin_id); @@ -577,22 +584,8 @@ get_psec(sc_card_t *card, const char *pin, size_t length_pin, u8 pin_id) memset(&hints, 0, sizeof(hints)); hints.dialog_name = "ccid.PACE"; hints.card = card; - switch (pin_id) { - case PACE_MRZ: - hints.prompt = "Enter MRZ"; - break; - case PACE_CAN: - hints.prompt = "Enter CAN"; - break; - case PACE_PIN: - hints.prompt = "Enter PIN"; - break; - case PACE_PUK: - hints.prompt = "Enter PUK"; - break; - default: - return NULL; - } + hints.prompt = NULL; + hints.obj_label = pace_secret_name(pin_id); hints.usage = SC_UI_USAGE_OTHER; sc_result = sc_ui_get_pin(&hints, &p); if (sc_result < 0) { @@ -601,9 +594,12 @@ get_psec(sc_card_t *card, const char *pin, size_t length_pin, u8 pin_id) return NULL; } - r = PACE_SEC_new(p, strlen(p), pin_id); + len = strlen(p); + r = PACE_SEC_new(p, len, pin_id); - OPENSSL_cleanse(p, strlen(p)); + if (len) { + OPENSSL_cleanse(p, len); + } free(p); return r; @@ -838,11 +834,33 @@ err: SC_FUNC_RETURN(card->ctx, SC_LOG_TYPE_DEBUG, r); } -int pace_test(sc_card_t *card) +static const char *MRZ_name = "MRZ"; +static const char *PIN_name = "PIN"; +static const char *PUK_name = "PUK"; +static const char *CAN_name = "CAN"; +static const char *UNDEF_name = "UNDEF"; +const char *pace_secret_name(enum s_type pin_id) { + switch (pin_id) { + case PACE_MRZ: + return MRZ_name; + case PACE_PUK: + return PUK_name; + case PACE_PIN: + return PIN_name; + case PACE_CAN: + return CAN_name; + default: + return UNDEF_name; + } +} + +int pace_test(sc_card_t *card, + enum s_type pin_id, const char *pin, size_t pinlen) { - __u8 in[16], buf[SC_MAX_APDU_BUFFER_SIZE - 2]; + u8 buf[0xff + 5]; + char *read = NULL; __u8 *out = NULL; - size_t outlen; + size_t outlen, readlen = 0, linelen, apdulen; struct sm_ctx sctx; sc_apdu_t apdu; int r; @@ -850,68 +868,76 @@ int pace_test(sc_card_t *card) memset(&sctx, 0, sizeof(sctx)); memset(&apdu, 0, sizeof(apdu)); - in[0] = PACE_CAN; // pin_id - in[1] = 0; // length_chat - in[2] = 6; // length_pin - in[3] = '8'; - in[4] = '4'; - in[5] = '0'; - in[6] = '1'; - in[7] = '7'; - in[8] = '9'; - in[9] = 0; // length_cert_desc - in[10]= 0; // length_cert_desc + switch (pin_id) { + case PACE_MRZ: + case PACE_CAN: + case PACE_PIN: + case PACE_PUK: + break; + default: + sc_error(card->ctx, "Type of secret not supported"); + return SC_ERROR_INVALID_ARGUMENTS; + } - SC_TEST_RET(card->ctx, EstablishPACEChannel(card, in, &out, &outlen, &sctx), + if (pinlen > sizeof(buf) - 5) { + sc_error(card->ctx, "%s too long (maximal %u bytes supported)", + pace_secret_name(pin_id), + sizeof(buf) - 5); + } + buf[0] = pin_id; + buf[1] = 0; // length_chat + buf[2] = pinlen; // length_pin + memcpy(&buf[3], pin, pinlen); + buf[3 + pinlen] = 0; // length_cert_desc + buf[4 + pinlen]= 0; // length_cert_desc + + SC_TEST_RET(card->ctx, + EstablishPACEChannel(card, buf, &out, &outlen, &sctx), "Could not establish PACE channel."); - /* select CardSecurity */ - apdu.cla = 0x00; - apdu.ins = 0xA4; - apdu.p1 = 0x08; - apdu.p2 = 0x00; - in[0] = 0x01; - in[1] = 0x1D; - apdu.data = in; - apdu.datalen = 2; - apdu.lc = apdu.datalen; - apdu.le = 0x00; - apdu.resp = buf; - apdu.resplen = sizeof buf; - apdu.cse = SC_APDU_CASE_4_SHORT; - r = pace_transmit_apdu(&sctx, card, &apdu); - if (r < 0) - sc_error(card->ctx, "Could not select EF.CardSecurity: %s", - sc_strerror(r)); + while (1) { + printf("Enter unencrypted APDU (empty line to exit)\n"); - /* read CardSecurity */ - apdu.cla = 0x00; - apdu.ins = 0xB0; - apdu.p1 = 0x02; - apdu.p2 = 0x00; - apdu.le = 0x00; - apdu.resp = buf; - apdu.resplen = sizeof buf; - apdu.cse = SC_APDU_CASE_2_SHORT; - r = pace_transmit_apdu(&sctx, card, &apdu); - if (r < 0) - sc_error(card->ctx, "Could not read EF.CardSecurity: %s", - sc_strerror(r)); + linelen = getline(&read, &readlen, stdin); + if (linelen <= 1) { + if (linelen < 0) { + r = SC_ERROR_INTERNAL; + sc_error(card->ctx, "Could not read line"); + } else { + r = SC_SUCCESS; + printf("Thanks for flying with ccid\n"); + } + break; + } - /* reset retry counter */ - apdu.cla = 0x00; - apdu.ins = 0x2C; - apdu.p1 = 0x03; - apdu.p2 = 0x02; - apdu.resp = buf; - apdu.resplen = sizeof buf; - apdu.cse = SC_APDU_CASE_1; - r = pace_transmit_apdu(&sctx, card, &apdu); - if (r < 0) - sc_error(card->ctx, "Could not reset retry counter of CAN: %s", - sc_strerror(r)); + read[linelen - 1] = 0; + if (sc_hex_to_bin(read, buf, &apdulen) < 0) { + sc_error(card->ctx, "Could not format binary string"); + } - return SC_SUCCESS; + r = build_apdu(buf, apdulen, &apdu); + if (r < 0) { + sc_error(card->ctx, "Could not format APDU"); + continue; + } + + apdu.resp = buf; + apdu.resplen = sizeof(buf); + + r = pace_transmit_apdu(&sctx, card, &apdu); + if (r < 0) { + sc_error(card->ctx, "Could not send APDU: %s", sc_strerror(r)); + continue; + } + + printf("Decrypted APDU sw1=%02x sw2=%02x\n", apdu.sw1, apdu.sw2); + bin_print(stdout, "Decrypted APDU response data", apdu.resp, apdu.resplen); + } + + if (read) + free(read); + + SC_FUNC_RETURN(card->ctx, SC_LOG_TYPE_ERROR, r); } static int diff --git a/ccid/pace.h b/ccid/pace.h index c565cad..edb702b 100644 --- a/ccid/pace.h +++ b/ccid/pace.h @@ -62,6 +62,8 @@ int increment_ssc(struct pace_sm_ctx *psmctx); int decrement_ssc(struct pace_sm_ctx *psmctx); int reset_ssc(struct pace_sm_ctx *psmctx); +const char *pace_secret_name(enum s_type pin_id); + int pace_sm_encrypt(sc_card_t *card, const struct sm_ctx *ctx, const u8 *data, size_t datalen, u8 **enc); int pace_sm_decrypt(sc_card_t *card, const struct sm_ctx *ctx, @@ -76,7 +78,8 @@ int GetReadersPACECapabilities(sc_card_t *card, const __u8 *in, __u8 **out, size_t *outlen); int EstablishPACEChannel(sc_card_t *card, const __u8 *in, __u8 **out, size_t *outlen, struct sm_ctx *ctx); -int pace_test(sc_card_t *card); +int pace_test(sc_card_t *card, + enum s_type pin_id, const char *pin, size_t pinlen); int pace_transmit_apdu(struct sm_ctx *sctx, sc_card_t *card, sc_apdu_t *apdu); diff --git a/ccid/sm.c b/ccid/sm.c index 864b65b..c4267c3 100644 --- a/ccid/sm.c +++ b/ccid/sm.c @@ -45,15 +45,22 @@ static const struct sc_asn1_entry c_sm_rapdu[] = { }; void _bin_log(sc_context_t *ctx, int type, const char *file, int line, - const char *func, const char *label, const u8 *data, size_t len) + const char *func, const char *label, const u8 *data, size_t len, + FILE *f) { char buf[1024]; sc_hex_dump(ctx, data, len, buf, sizeof buf); - sc_do_log(ctx, type, file, line, func, - "\n%s (%u byte%s):\n%s" - "======================================================================", - label, len, len==1?"":"s", buf); + if (!f) { + sc_do_log(ctx, type, file, line, func, + "\n%s (%u byte%s):\n%s" + "======================================================================", + label, len, len==1?"":"s", buf); + } else { + fprintf(f, "%s (%u byte%s):\n%s" + "======================================================================\n", + label, len, len==1?"":"s", buf); + } } int diff --git a/ccid/sm.h b/ccid/sm.h index a15e993..4e1b7ce 100644 --- a/ccid/sm.h +++ b/ccid/sm.h @@ -53,10 +53,13 @@ int add_iso_pad(const u8 *data, size_t datalen, int block_size, u8 **padded); int add_padding(const struct sm_ctx *ctx, const u8 *data, size_t datalen, u8 **padded); +#define bin_print(file, label, data, len) \ + _bin_log(NULL, 0, NULL, 0, NULL, label, data, len, file) #define bin_log(ctx, label, data, len) \ - _bin_log(ctx, SC_LOG_TYPE_DEBUG, __FILE__, __LINE__, __FUNCTION__, label, data, len) + _bin_log(ctx, SC_LOG_TYPE_DEBUG, __FILE__, __LINE__, __FUNCTION__, label, data, len, NULL) void _bin_log(sc_context_t *ctx, int type, const char *file, int line, - const char *func, const char *label, const u8 *data, size_t len); + const char *func, const char *label, const u8 *data, size_t len, + FILE *f); #ifdef __cplusplus } diff --git a/ccid/usb.c b/ccid/usb.c index 75a94af..8e88ba6 100644 --- a/ccid/usb.c +++ b/ccid/usb.c @@ -47,7 +47,7 @@ #include "usbstring.h" #include "ccid.h" - +#include "pace.h" #define DRIVER_VENDOR_NUM 0x0D46 /* KOBIL Systems */ #define DRIVER_PRODUCT_NUM 0x3010 /* KOBIL Class 3 Reader */ @@ -57,31 +57,51 @@ static int verbose = 0; static int debug = 0; static int dohid = 0; static int doint = 0; -static int dotest = 0; +static u8 dopacetest = 0; static int usb_reader_num = -1; +static const char *pin; + +#define OPT_HELP 'h' +#define OPT_INTERRUPT 'n' +#define OPT_READER 'r' +#define OPT_SERIAL 's' +#define OPT_PIN 'i' +#define OPT_PUK 'u' +#define OPT_CAN 'a' +#define OPT_MRZ 'z' +#define OPT_PRODUCT 'p' +#define OPT_VENDOR 'e' +#define OPT_VERBOSE 'v' +#define OPT_VERSION 'o' static const struct option options[] = { /*{ "hid", no_argument, &dohid, 1 },*/ - { "help", no_argument, NULL, 'h' }, - { "interrupt", no_argument, &doint, 'n' }, - { "reader", required_argument, NULL, 'r' }, - { "serial", required_argument, NULL, 's' }, - { "test-pace", no_argument, &dotest, 't' }, - { "product", required_argument, NULL, 'p' }, - { "vendor", required_argument, NULL, 'e' }, - { "verbose", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, 'i' }, + { "help", no_argument, NULL, OPT_HELP }, + { "reader", required_argument, NULL, OPT_READER }, + { "test-pin", optional_argument, NULL, OPT_PIN }, + { "test-puk", optional_argument, NULL, OPT_PUK }, + { "test-can", optional_argument, NULL, OPT_CAN }, + { "test-mrz", optional_argument, NULL, OPT_MRZ }, + { "serial", required_argument, NULL, OPT_SERIAL }, + { "product", required_argument, NULL, OPT_PRODUCT }, + { "vendor", required_argument, NULL, OPT_VENDOR }, + { "interrupt", no_argument, &doint, OPT_INTERRUPT }, + { "verbose", no_argument, NULL, OPT_VERBOSE }, + { "version", no_argument, NULL, OPT_VERSION }, { NULL, 0, NULL, 0 } }; static const char *option_help[] = { /*"Emulate HID device",*/ "Print help and exit", + "Number of reader to use (default: auto-detect)", + "Run PACE with PIN", + "Run PACE with PUK", + "Run PACE with CAN", + "Run PACE with MRZ", + "USB serial number (default: random)", + "USB product ID (default: 0x3010)", + "USB vendor ID (default: 0x0D46)", "Add interrupt pipe for CCID", - "Number of the reader to use (default: auto-detect)", - "Serial number (max 56 bytes) of gadget (default: random)", - "test PACE implementation", - "USB product ID (default: 0x3010)", - "USB vendor ID (0default: x0D46)", "Use (several times) to be more verbose", "Print version and exit.", }; @@ -1081,7 +1101,6 @@ error: static void *hid (void *param) { - printf("%s:%d\n", __FILE__, __LINE__); char **names = (char **) param; char *status_name = names[0]; int result = 0; @@ -1718,11 +1737,11 @@ void print_usage(const char *app_name, const struct option options[], break; } sprintf(buf, "--%s%s%s", options[i].name, tmp, arg_str); - if (strlen(buf) > 20) { + if (strlen(buf) > 22) { printf(" %s\n", buf); buf[0] = '\0'; } - printf(" %-20s %s\n", buf, option_help[i]); + printf(" %-22s %s\n", buf, option_help[i]); i++; } } @@ -1749,41 +1768,72 @@ main (int argc, char **argv) } while (1) { - c = getopt_long(argc, argv, "r:s:p:e:vih", options, &long_optind); + c = getopt_long(argc, argv, "hnr:s:i::u::a::z::p:e:vo", options, &long_optind); if (c == -1) break; - if (c == '?' || c == 'h') { + if (c == '?' || c == OPT_HELP) { print_usage(argv[0] , options, option_help); exit(0); } switch (c) { - case 'r': + case OPT_READER: if (sscanf(optarg, "%d", &usb_reader_num) != 1) parse_error(argv[0], optarg, long_optind); break; - case 's': + case OPT_SERIAL: if (sscanf(optarg, "%56s", serial) != 1) parse_error(argv[0], optarg, long_optind); break; - case 'p': + case OPT_PRODUCT: if (sscanf(optarg, "%x", &productid) != 1) parse_error(argv[0], optarg, long_optind); break; - case 'e': + case OPT_VENDOR: if (sscanf(optarg, "%x", &vendorid) != 1) parse_error(argv[0], optarg, long_optind); break; - case 'v': + case OPT_VERBOSE: verbose++; break; - case 'i': + case OPT_VERSION: fprintf(stderr, "%s 0.9 written by Frank Morgner.\n" , argv[0]); exit(0); break; + case OPT_PUK: + /* PACE_PIN from openssl/pace.h */ + dopacetest = 4; + pin = optarg; + break; + case OPT_PIN: + /* PACE_PIN from openssl/pace.h */ + dopacetest = 3; + pin = optarg; + break; + case OPT_CAN: + /* PACE_PIN from openssl/pace.h */ + dopacetest = 2; + pin = optarg; + break; + case OPT_MRZ: + /* PACE_PIN from openssl/pace.h */ + dopacetest = 1; + pin = optarg; + break; } } + if (ccid_initialize(usb_reader_num, verbose) < 0) { + perror("Can't initialize ccid"); + return 1; + } + + if (dopacetest) { + i = ccid_testpace(dopacetest, pin, !pin ? 0 : strlen(pin)); + ccid_shutdown(); + return i; + } + if (verbose) fprintf (stderr, "serial=\"%s\"\n", serial); @@ -1792,17 +1842,6 @@ main (int argc, char **argv) return 1; } - if (ccid_initialize(usb_reader_num, verbose) < 0) { - perror("can't initialize ccid"); - return 1; - } - - if (dotest) { - i = ccid_testpace(); - ccid_shutdown(); - return i; - } - fd = init_device (); if (fd < 0) return 1;