- Fixed error when testing pace without card

- Testing pace allows now to choose the type of the secret (mrz, pin, can, puk).
  The secret can be given as commandline option or interactively.
- Cleaned prompt for getting the password
- Fixed error when inserting an empty password
- When PACE is successfully finished, APDUs for testing can be given
  interactively


git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@73 96b47cad-a561-4643-ad3b-153ac7d7599c
This commit is contained in:
frankmorgner
2010-04-15 07:37:32 +00:00
parent b798a6e3a2
commit 35f75abe1d
8 changed files with 248 additions and 140 deletions

View File

@@ -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
---------

View File

@@ -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;
}

View File

@@ -20,6 +20,7 @@
#define _CCID_H
#include <linux/usb/ch9.h>
#include <opensc/opensc.h>
#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
}

View File

@@ -18,6 +18,8 @@
*/
#include "pace.h"
#include "sm.h"
#include "ccid.h"
#include <stdio.h>
#include <opensc/log.h>
#include <openssl/evp.h>
#include <openssl/asn1.h>
@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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
}

View File

@@ -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;