From e78dc964403cc301bc19ccf0607beff6ffff4f26 Mon Sep 17 00:00:00 2001 From: jey Date: Thu, 21 Feb 2002 18:53:23 +0000 Subject: [PATCH] - added ATR for RSA SecurID 3100 - exported pkcs15_encode_* functions - minor modification to sc_file_add_acl_entry() - boosted up opensc-explorer - added error(), warn() and fatal() - implemented a generic PKCS #15 structure generation tool git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@215 c6295689-39f2-0310-b995-f0e70906c6a9 --- src/libopensc/Makefile.am | 3 +- src/libopensc/card-gpk.c | 489 ++++++++++++++++++++++++---------- src/libopensc/card-setcos.c | 2 + src/libopensc/card.c | 13 + src/libopensc/opensc-pkcs15.h | 9 + src/libopensc/opensc.h | 7 +- src/libopensc/pkcs15.c | 2 +- src/libopensc/pkcs15.h | 9 + src/libopensc/sc.c | 11 +- src/tools/Makefile.am | 7 +- src/tools/opensc-explorer.c | 140 +++++----- src/tools/util.c | 38 +++ src/tools/util.h | 3 + 13 files changed, 511 insertions(+), 222 deletions(-) diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index b233671f..ec7d7e1c 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -20,5 +20,6 @@ else libopensc_la_LIBADD = @LIBPCSC@ endif -include_HEADERS = opensc.h opensc-pkcs15.h opensc-emv.h +include_HEADERS = opensc.h opensc-pkcs15.h opensc-emv.h \ + cardctl.h noinst_HEADERS = sc-asn1.h sc-log.h sc-internal.h diff --git a/src/libopensc/card-gpk.c b/src/libopensc/card-gpk.c index 447edd0a..f5e74995 100644 --- a/src/libopensc/card-gpk.c +++ b/src/libopensc/card-gpk.c @@ -20,17 +20,23 @@ #include "sc-internal.h" #include "sc-log.h" -#if defined(HAVE_OPENSSL) && 0 +#include "cardctl.h" + +#ifdef HAVE_OPENSSL #include +#include #include #include -/* GPK4000 variants */ +/* Gemplus card variants */ enum { GPK4000_su256, GPK4000_s, GPK4000_sp, GPK4000_sdo, + GPK8000, + GPK8000_8K, + GPK8000_16K, }; #define GPK_SEL_MF 0x00 @@ -39,6 +45,10 @@ enum { #define GPK_SEL_AID 0x04 #define GPK_FID_MF 0x3F00 +#define GPK_FTYPE_SC 0x21 + +#define GPK_MAX_PINS 8 + /* * GPK4000 private data */ @@ -50,9 +60,8 @@ struct gpk_private_data { /* is non-zero if we should use secure messaging */ unsigned key_set : 1; - unsigned key_local : 1, - key_sfi : 5; - u8 key[16]; + unsigned int key_reference; + u8 key[16]; }; #define OPSDATA(card) ((struct gpk_private_data *) ((card)->ops_data)) @@ -147,22 +156,30 @@ gpk_finish(struct sc_card *card) * sc_check_sw doesn't seem to handle all of the * status words the GPK is capable of returning */ +#if 0 static int -gpk_sw_to_errorcode(struct sc_card *card, u8 sw1, u8 sw2) +gpk_check_sw(struct sc_card *card, u8 sw1, u8 sw2) { unsigned short int sw = (sw1 << 8) | sw2; if ((sw & 0xFFF0) == 0x63C0) { - error(card->ctx, "wrong PIN, %u tries left", sw&0xf); + error(card->ctx, "wrong PIN, %u tries left\n", sw&0xf); return SC_ERROR_PIN_CODE_INCORRECT; } switch (sw) { case 0x6400: - error(card->ctx, "wrong crypto context"); + error(card->ctx, "wrong crypto context\n"); return SC_ERROR_OBJECT_NOT_VALID; /* XXX ??? */ + + /* The following are handled by iso7816_check_sw + * but all return SC_ERROR_UNKNOWN_REPLY + * XXX: fix in the iso driver? */ + case 0x6983: + error(card->ctx, "PIN is blocked\n"); + return SC_ERROR_PIN_CODE_INCORRECT; case 0x6581: - error(card->ctx, "out of space on card or file"); + error(card->ctx, "out of space on card or file\n"); return SC_ERROR_OUT_OF_MEMORY; case 0x6981: return SC_ERROR_FILE_NOT_FOUND; @@ -171,8 +188,9 @@ gpk_sw_to_errorcode(struct sc_card *card, u8 sw1, u8 sw2) return SC_ERROR_INVALID_ARGUMENTS; } - return sc_check_sw(card, sw1, sw2); + return iso7816_check_sw(card, sw1, sw2); } +#endif /* * Select a DF/EF @@ -202,7 +220,7 @@ match_path(struct sc_card *card, unsigned short int **pathptr, size_t *pathlen, if (len < curlen) return 0; - for (i = 0; i < len; i++) { + for (i = 0; i < curlen; i++) { if (ptr[i] != curptr[i]) return 0; } @@ -216,30 +234,28 @@ match_path(struct sc_card *card, unsigned short int **pathptr, size_t *pathlen, return 1; } -static inline unsigned int -ac_to_acl(unsigned short int ac) +static void +ac_to_acl(u_int16_t ac, struct sc_file *file, unsigned int op) { unsigned int npins, pin; - unsigned int res = 0; npins = (ac >> 14) & 3; - if (npins == 3) - return SC_AC_NEVER; - pin = ac & 0xFF; - while (npins--) { - switch (pin & 7) { - case 0: res |= SC_AC_CHV1; break; - case 1: res |= SC_AC_CHV2; break; - default:return SC_AC_NEVER; - } - pin >>= 4; + if (npins == 3) { + sc_file_add_acl_entry(file, op, SC_AC_NEVER, + SC_AC_KEY_REF_NONE); + return; } - /* Check whether secure messaging key is specified */ - if (ac & 0x1F00) - res |= SC_AC_PRO; + sc_file_add_acl_entry(file, op, SC_AC_NONE, SC_AC_KEY_REF_NONE); + pin = ac & 0xFF; + if (npins >= 1) + sc_file_add_acl_entry(file, op, SC_AC_CHV, (pin >> 4) & 0xF); + if (npins == 2) + sc_file_add_acl_entry(file, op, SC_AC_CHV, pin & 0xF); - return res; + /* Check whether secure messaging key is specified */ + if (ac & 0x3F00) + sc_file_add_acl_entry(file, op, SC_AC_PRO, (ac & 0x3F00) >> 8); } /* @@ -247,36 +263,38 @@ ac_to_acl(unsigned short int ac) * bits supported by the GPK. Since these do not map 1:1 there's * some fuzz involved. */ -static inline void -acl_to_ac(unsigned int acl, u8 *ac) +static void +acl_to_ac(struct sc_file *file, unsigned int op, u8 *ac) { + const struct sc_acl_entry *acl; + unsigned int npins = 0; + ac[0] = ac[1] = 0; - if (acl == SC_AC_NEVER) { + acl = sc_file_get_acl_entry(file, op); + + assert(acl->method != SC_AC_UNKNOWN); + switch (acl->method) { + case SC_AC_NEVER: ac[0] = 0xC0; return; + case SC_AC_NONE: + return; } - /* XXX should we set the "local" flag for PINs or not? - * OpenSC does not provide for a "lock file" operation - * that lets us freeze the ac bits after setting up the file. - */ - if (acl & SC_AC_CHV2) { - ac[0] += 0x40; - ac[1] |= 1; - } - if (acl & SC_AC_CHV1) { - ac[0] += 0x40; - ac[1] <<= 4; - ac[1] |= 0; - } - - /* XXX should we set the "local" flag on key files or not? - * OpenSC does not provide for a "lock file" operation - * that lets us freeze the ac bits after setting up the file. - */ - if (acl & SC_AC_PRO) { - ac[0] |= 0x01; + while (acl) { + if (acl->method == SC_AC_CHV) { + /* Support up to 2 PINS only */ + if (++npins >= 2) + continue; + ac[1] >>= 4; + ac[1] |= acl->key_ref << 4; + ac[0] += 0x40; + } + if (acl->method == SC_AC_PRO) { + ac[0] |= acl->key_ref & 0x1f; + } + acl = acl->next; } } @@ -285,13 +303,12 @@ gpk_parse_fileinfo(struct sc_card *card, const u8 *buf, size_t buflen, struct sc_file *file) { - struct gpk_private_data *priv = OPSDATA(card); const u8 *sp, *end, *next; int i; memset(file, 0, sizeof(*file)); for (i = 0; i < SC_MAX_AC_OPS; i++) - file->acl[i] = SC_AC_UNKNOWN; + sc_file_add_acl_entry(file, i, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE); end = buf + buflen; for (sp = buf; sp + 2 < end; sp = next) { @@ -306,19 +323,16 @@ gpk_parse_fileinfo(struct sc_card *card, memcpy(file->name, sp+2, sp[1]); } else if (sp[0] == 0x85) { - unsigned int ac1, ac2, ac3; + unsigned int ac[3], n; file->id = (sp[4] << 8) | sp[5]; file->size = (sp[8] << 8) | sp[9]; file->record_length = sp[7]; - /* Map ACLs */ - priv->ac[0] = (sp[10] << 8) | sp[11]; - priv->ac[1] = (sp[12] << 8) | sp[13]; - priv->ac[2] = (sp[14] << 8) | sp[15]; /* EF only */ - ac1 = ac_to_acl(priv->ac[0]); - ac2 = ac_to_acl(priv->ac[1]); - ac3 = ac_to_acl(priv->ac[2]); + /* Map ACLs. Note the third AC byte is + * valid of EFs only */ + for (n = 0; n < 3; n++) + ac[n] = (sp[10+2*n] << 8) | sp[11+2*n]; /* Examine file type */ switch (sp[6] & 7) { @@ -326,22 +340,27 @@ gpk_parse_fileinfo(struct sc_card *card, case 0x05: case 0x06: case 0x07: file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = sp[6] & 7; - file->acl[SC_AC_OP_READ] = ac3; - file->acl[SC_AC_OP_WRITE] = ac3; - file->acl[SC_AC_OP_UPDATE] = ac1; + ac_to_acl(ac[0], file, SC_AC_OP_UPDATE); + ac_to_acl(ac[1], file, SC_AC_OP_WRITE); + ac_to_acl(ac[2], file, SC_AC_OP_READ); break; case 0x00: /* 0x38 is DF */ file->type = SC_FILE_TYPE_DF; - file->acl[SC_AC_OP_SELECT] = SC_AC_NONE; - file->acl[SC_AC_OP_LOCK] = ac1; /* Icky: the GPK uses different ACLs * for creating data files and * 'sensitive' i.e. key files */ - file->acl[SC_AC_OP_CREATE] = ac2; - file->acl[SC_AC_OP_DELETE] = SC_AC_NEVER; - file->acl[SC_AC_OP_REHABILITATE] = SC_AC_NEVER; - file->acl[SC_AC_OP_INVALIDATE] = SC_AC_NEVER; - file->acl[SC_AC_OP_LIST_FILES] = SC_AC_NEVER; + ac_to_acl(ac[0], file, SC_AC_OP_LOCK); + ac_to_acl(ac[1], file, SC_AC_OP_CREATE); + sc_file_add_acl_entry(file, SC_AC_OP_SELECT, + SC_AC_NONE, SC_AC_KEY_REF_NONE); + sc_file_add_acl_entry(file, SC_AC_OP_DELETE, + SC_AC_NEVER, SC_AC_KEY_REF_NONE); + sc_file_add_acl_entry(file, SC_AC_OP_REHABILITATE, + SC_AC_NEVER, SC_AC_KEY_REF_NONE); + sc_file_add_acl_entry(file, SC_AC_OP_INVALIDATE, + SC_AC_NEVER, SC_AC_KEY_REF_NONE); + sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES, + SC_AC_NEVER, SC_AC_KEY_REF_NONE); break; } } @@ -357,11 +376,11 @@ gpk_parse_fileinfo(struct sc_card *card, static int gpk_select(struct sc_card *card, u8 kind, const u8 *buf, size_t buflen, - struct sc_file *file) + struct sc_file **file) { struct gpk_private_data *priv = OPSDATA(card); struct sc_apdu apdu; - u8 resbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 resbuf[SC_MAX_APDU_BUFFER_SIZE]; int r; /* If we're about to select a DF, invalidate secure messaging keys */ @@ -371,7 +390,12 @@ gpk_select(struct sc_card *card, u8 kind, } /* do the apdu thing */ - sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, kind, 0); + memset(&apdu, 0, sizeof(apdu)); + apdu.cla = 0x00; + apdu.cse = SC_APDU_CASE_3_SHORT; + apdu.ins = 0xA4; + apdu.p1 = kind; + apdu.p2 = 0; apdu.data = buf; apdu.datalen = buflen; apdu.lc = apdu.datalen; @@ -380,7 +404,7 @@ gpk_select(struct sc_card *card, u8 kind, r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, r, "APDU transmit failed"); - r = gpk_sw_to_errorcode(card, apdu.sw1, apdu.sw2); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, r, "Card returned error"); /* Nothing we can say about it... invalidate @@ -391,21 +415,34 @@ gpk_select(struct sc_card *card, u8 kind, if (file == NULL) return 0; + *file = sc_file_new(); - return gpk_parse_fileinfo(card, apdu.resp, apdu.resplen, file); + r = gpk_parse_fileinfo(card, apdu.resp, apdu.resplen, *file); + if (r < 0) { + sc_file_free(*file); + *file = NULL; + } + return r; } static int gpk_select_id(struct sc_card *card, u8 kind, unsigned short int fid, - struct sc_file *file) + struct sc_file **file) { struct sc_path *cp = &card->cache.current_path; - u8 fbuf[2]; - int r; + u8 fbuf[2]; + int r, log_errs; + + if (card->ctx->debug) + debug(card->ctx, "gpk_select_id(0x%04X)\n", fid); fbuf[0] = fid >> 8; fbuf[1] = fid & 0xff; + + log_errs = card->ctx->log_errors; + card->ctx->log_errors = 0; r = gpk_select(card, kind, fbuf, 2, file); + card->ctx->log_errors = log_errs; /* Fix up the path cache */ if (r == 0) { @@ -428,15 +465,15 @@ gpk_select_id(struct sc_card *card, u8 kind, unsigned short int fid, static int gpk_select_file(struct sc_card *card, const struct sc_path *path, - struct sc_file *file) + struct sc_file **file) { unsigned short int pathtmp[SC_MAX_PATH_SIZE/2]; unsigned short int *pathptr; - size_t pathlen, n; - int locked = 0, r = 0, use_relative = 0; - u8 leaf_type; + size_t pathlen, n; + int locked = 0, r = 0, use_relative = 0, retry = 1; + u8 leaf_type; - SC_FUNC_CALLED(card->ctx, 3); + SC_FUNC_CALLED(card->ctx, 1); /* Handle the AID case first */ if (path->type == SC_PATH_TYPE_DF_NAME) { @@ -467,6 +504,10 @@ try_again: /* See whether we can skip an initial portion of the * (absolute) path */ if (path->type == SC_PATH_TYPE_PATH) { + /* Do not retry selecting if this cannot be a DF */ + if ((pathptr[0] == GPK_FID_MF && pathlen > 2) + || (pathptr[0] != GPK_FID_MF && pathlen > 1)) + retry = 0; use_relative = match_path(card, &pathptr, &pathlen, file != 0); if (pathlen == 0) goto done; @@ -521,7 +562,7 @@ try_again: if (r) { /* Did we guess EF, and were wrong? If so, invalidate * path cache and try again; this time aiming for a DF */ - if (leaf_type == GPK_SEL_EF) { + if (leaf_type == GPK_SEL_EF && retry) { card->cache.current_path.len = 0; leaf_type = GPK_SEL_DF; goto try_again; @@ -543,7 +584,7 @@ gpk_compute_crycks(struct sc_card *card, struct sc_apdu *apdu, { struct gpk_private_data *priv = OPSDATA(card); des_key_schedule k1, k2; - u8 in[8], out[8], block[64]; + u8 in[8], out[8], block[64]; unsigned int len = 0, i, j; /* Set the key schedule */ @@ -577,7 +618,7 @@ gpk_compute_crycks(struct sc_card *card, struct sc_apdu *apdu, memcpy((u8 *) (apdu->data + apdu->datalen), out + 5, 3); apdu->datalen += 3; apdu->lc += 3; - apdu->le = 3; + apdu->le += 3; if (crycks1) memcpy(crycks1, out, 3); memset(k1, 0, sizeof(k1)); @@ -588,6 +629,22 @@ gpk_compute_crycks(struct sc_card *card, struct sc_apdu *apdu, return 0; } +/* + * Verify secure messaging response + */ +static int +gpk_verify_crycks(struct sc_card *card, struct sc_apdu *apdu, u8 *crycks) +{ + if (apdu->resplen < 3 + || memcmp(apdu->resp + apdu->resplen - 3, crycks, 3)) { + if (card->ctx->debug) + debug(card->ctx, "Invalid secure messaging reply\n"); + return SC_ERROR_UNKNOWN_REPLY; + } + apdu->resplen -= 3; + return 0; +} + /* * Create a file or directory. * This is a bit tricky because we abuse the ef_structure @@ -599,10 +656,13 @@ gpk_create_file(struct sc_card *card, struct sc_file *file) { struct gpk_private_data *priv = OPSDATA(card); struct sc_apdu apdu; - u8 data[28+3], crycks[3], resp[3]; + u8 data[28+3], crycks[3], resp[3]; size_t datalen, namelen; int r; + if (card->ctx->debug) + debug(card->ctx, "gpk_create_file(0x%04X)\n", file->id); + /* Prepare APDU */ memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x80; /* assume no secure messaging */ @@ -627,8 +687,8 @@ gpk_create_file(struct sc_card *card, struct sc_file *file) */ apdu.p1 = 0x01; /* create DF */ data[2] = 0x38; - acl_to_ac(file->acl[SC_AC_OP_CREATE], data + 6); - acl_to_ac(file->acl[SC_AC_OP_CREATE], data + 8); + acl_to_ac(file, SC_AC_OP_CREATE, data + 6); + acl_to_ac(file, SC_AC_OP_CREATE, data + 8); if ((namelen = file->namelen) != 0) { if (namelen > 16) return SC_ERROR_INVALID_ARGUMENTS; @@ -642,9 +702,9 @@ gpk_create_file(struct sc_card *card, struct sc_file *file) data[3] = file->record_length; data[4] = file->size >> 8; data[5] = file->size & 0xff; - acl_to_ac(file->acl[SC_AC_OP_UPDATE], data + 6); - acl_to_ac(file->acl[SC_AC_OP_WRITE], data + 8); - acl_to_ac(file->acl[SC_AC_OP_READ], data + 10); + acl_to_ac(file, SC_AC_OP_UPDATE, data + 6); + acl_to_ac(file, SC_AC_OP_WRITE, data + 8); + acl_to_ac(file, SC_AC_OP_READ, data + 10); } apdu.data = data; @@ -663,16 +723,12 @@ gpk_create_file(struct sc_card *card, struct sc_file *file) r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, r, "APDU transmit failed"); - r = gpk_sw_to_errorcode(card, apdu.sw1, apdu.sw2); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, r, "Card returned error"); - if (priv->key_set) { - /* verify CRYCKS response? */ - if (apdu.resplen != 3 - || memcmp(resp, crycks, 3)) { - printf("XXX Secure messaging: bad resp\n"); - } - } + /* verify secure messaging response */ + if (priv->key_set) + r = gpk_verify_crycks(card, &apdu, crycks); return r; } @@ -718,59 +774,36 @@ gpk_set_filekey(const u8 *key, const u8 *challenge, * Verify a key presented by the user for secure messaging */ static int -gpk_select_key(struct sc_card *card, int ref, const u8 *buf, size_t buflen) +gpk_select_key(struct sc_card *card, int key_sfi, const u8 *buf, size_t buflen) { struct gpk_private_data *priv = OPSDATA(card); struct sc_apdu apdu; - u8 random[8], resp[258]; - unsigned int n, sfi, key_sfi = 0; + u8 random[8], resp[258]; int r; + SC_FUNC_CALLED(card->ctx, 1); + if (buflen != 16) return SC_ERROR_INVALID_ARGUMENTS; - /* The opensc API doesn't tell us what key it wants to - * select, and why. We need to look at the ACs of - * the most recently selected file and guess - */ - key_sfi = 0; - for (n = 0; n < 3; n++) { - sfi = (priv->ac[n] >> 8) & 0x3F; - if (sfi & 0xF) { - if (key_sfi && key_sfi != sfi) { - /* Hm, the file has ACLs with two - * different keys. I'm unable to guess - * which one I should use, so I throw - * up my hands in disgust. - */ - /* XXX fix errror code? */ - return SC_ERROR_INVALID_ARGUMENTS; - } - key_sfi = sfi; - } - } - - /* If no key required, assume transport key :-/ */ - if (key_sfi == 0) - key_sfi = 0x01; - - /* XXX now do the SelFk */ + /* now do the SelFk */ RAND_pseudo_bytes(random, sizeof(random)); memset(&apdu, 0, sizeof(apdu)); apdu.cla = 0x80; apdu.cse = SC_APDU_CASE_4_SHORT; apdu.ins = 0x28; - apdu.p1 = ref << 1; + apdu.p1 = 0; apdu.p2 = key_sfi; apdu.data = random; apdu.datalen = sizeof(random); apdu.lc = apdu.datalen; apdu.resp = resp; apdu.resplen = sizeof(resp); + apdu.le = 12; r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, r, "APDU transmit failed"); - r = gpk_sw_to_errorcode(card, apdu.sw1, apdu.sw2); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); SC_TEST_RET(card->ctx, r, "Card returned error"); if (apdu.resplen != 12) { @@ -778,14 +811,63 @@ gpk_select_key(struct sc_card *card, int ref, const u8 *buf, size_t buflen) } else if ((r = gpk_set_filekey(buf, random, resp, priv->key)) == 0) { priv->key_set = 1; - priv->key_local = (key_sfi & 0x20)? 1 : 0; - priv->key_sfi = key_sfi & 0x1f; + priv->key_reference = key_sfi; } memset(resp, 0, sizeof(resp)); return r; } +/* + * Verify a PIN + * XXX Checking for a PIN from the global EFsc is quite hairy, + * because we can do this only when the MF is selected. + * So, simply don't do this :-) + */ +static int +gpk_verify_pin(struct sc_card *card, int ref, + const u8 *pin, size_t pinlen, int *tries_left) +{ + u_int8_t buffer[8]; + struct sc_apdu apdu; + int r; + + SC_FUNC_CALLED(card->ctx, 1); + + if (pinlen > 8) + return SC_ERROR_INVALID_PIN_LENGTH; + + /* Copy PIN, 0-padded */ + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, pin, pinlen); + + /* XXX deal with secure messaging here */ + memset(&apdu, 0, sizeof(apdu)); + apdu.cse = SC_APDU_CASE_3_SHORT; + apdu.cla = 0x00; + apdu.ins = 0x20; + apdu.p1 = 0x00; + apdu.p2 = ref & 7; + apdu.lc = 8; + apdu.datalen = 8; + apdu.data = buffer; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + /* Special case: extract tries_left */ + if (apdu.sw1 == 0x63 && (apdu.sw2 & 0xF0) == 0xC0) { + if (tries_left) + *tries_left = apdu.sw2 & 0xF; + return SC_ERROR_PIN_CODE_INCORRECT; + } + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, r, "Card returned error"); + + return r; +} + /* * Verify key (for external auth/secure messaging) or PIN * presented by the user @@ -799,10 +881,142 @@ gpk_verify(struct sc_card *card, unsigned int type, int ref, switch (type) { case SC_AC_PRO: return gpk_select_key(card, ref, buf, buflen); + case SC_AC_CHV: + return gpk_verify_pin(card, ref, buf, buflen, tries_left); } return SC_ERROR_INVALID_ARGUMENTS; } + +/* + * Erase card + */ +static int +gpk_erase_card(struct sc_card *card) +{ + struct gpk_private_data *priv = OPSDATA(card); + struct sc_apdu apdu; + u_int8_t offset; + int r; + + SC_FUNC_CALLED(card->ctx, 1); + switch (priv->variant) { + case GPK4000_su256: + case GPK4000_sdo: + offset = 0x6B; /* courtesy gemplus hotline */ + break; + + case GPK4000_s: + offset = 7; + break; + + case GPK8000: + case GPK8000_8K: + case GPK8000_16K: + offset = 0; + break; + + default: + return SC_ERROR_NOT_SUPPORTED; + } + + memset(&apdu, 0, sizeof(apdu)); + apdu.cse = SC_APDU_CASE_1; + apdu.cla = 0xDB; + apdu.ins = 0xDE; + apdu.p2 = offset; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, r, "Card returned error"); + + priv->key_set = 0; + SC_FUNC_RETURN(card->ctx, 2, r); +} + +/* + * Lock a file Access Condition. + * + * File must be selected, and we assume that any authentication + * that needs to be presented in order to allow this operation + * have been presented (ACs from the DF; AC1 for sensitive files, + * AC2 for normal files). + */ +static int +gpk_lock(struct sc_card *card, struct sc_cardctl_gpk_lock *args) +{ + struct gpk_private_data *priv = OPSDATA(card); + struct sc_file *file = args->file; + struct sc_apdu apdu; + u8 data[8], crycks[3], resp[3]; + int r; + + if (card->ctx->debug) + debug(card->ctx, "gpk_lock(0x%04X, %u)\n", + file->id, args->operation); + + memset(data, 0, sizeof(data)); + data[0] = file->id >> 8; + data[1] = file->id; + switch (args->operation) { + case SC_AC_OP_UPDATE: + data[2] = 0x40; break; + case SC_AC_OP_WRITE: + data[3] = 0x40; break; + case SC_AC_OP_READ: + data[4] = 0x40; break; + default: + return SC_ERROR_INVALID_ARGUMENTS; + } + + memset(&apdu, 0, sizeof(apdu)); + apdu.cse = SC_APDU_CASE_3_SHORT; + apdu.cla = 0x80; + apdu.ins = 0x16; + apdu.p1 = (file->type == SC_FILE_TYPE_DF)? 1 : 2; + apdu.p2 = 0; + apdu.lc = 5; + apdu.datalen = 5; + apdu.data = data; + + if (priv->key_set) { + apdu.cla = 0x84; + apdu.cse = SC_APDU_CASE_4_SHORT; + r = gpk_compute_crycks(card, &apdu, crycks); + if (r) + return r; + apdu.resp = resp; + apdu.resplen = sizeof(resp); /* XXX? */ + } + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, r, "Card returned error"); + + if (priv->key_set) + r = gpk_verify_crycks(card, &apdu, crycks); + + return r; +} + +/* + * Dispatch card_ctl calls + */ +static int +gpk_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) +{ + switch (cmd) { + case SC_CARDCTL_ERASE_CARD: + return gpk_erase_card(card); + case SC_CARDCTL_GPK_LOCK: + return gpk_lock(card, (struct sc_cardctl_gpk_lock *) ptr); + } + error(card->ctx, "card_ctl command %u not supported\n", cmd); + return SC_ERROR_NOT_SUPPORTED; +} + /* * Initialize the driver struct */ @@ -819,12 +1033,10 @@ sc_get_driver() gpk_ops.init = gpk_init; gpk_ops.finish = gpk_finish; gpk_ops.select_file = gpk_select_file; - /* The GPK4000 doesn't have a read directory command. */ -#if 0 - gpk_ops.list_files = gpk_list_files; -#endif gpk_ops.verify = gpk_verify; gpk_ops.create_file = gpk_create_file; + /* gpk_ops.check_sw = gpk_check_sw; */ + gpk_ops.card_ctl = gpk_card_ctl; } return &gpk_drv; } @@ -834,5 +1046,4 @@ sc_get_gpk_driver() { return sc_get_driver(); } - #endif /* HAVE_OPENSSL */ diff --git a/src/libopensc/card-setcos.c b/src/libopensc/card-setcos.c index 9f9ec57f..88cdea48 100644 --- a/src/libopensc/card-setcos.c +++ b/src/libopensc/card-setcos.c @@ -26,6 +26,8 @@ static const char *setec_atrs[] = { "3B:9F:94:40:1E:00:67:11:43:46:49:53:45:10:52:66:FF:81:90:00", /* this is from a Nokia branded SC */ "3B:1F:11:00:67:80:42:46:49:53:45:10:52:66:FF:81:90:00", + /* RSA SecurID 3100 */ + "3B:9F:94:40:1E:00:67:16:43:46:49:53:45:10:52:66:FF:81:90:00", NULL }; diff --git a/src/libopensc/card.c b/src/libopensc/card.c index ff1ff517..42047883 100644 --- a/src/libopensc/card.c +++ b/src/libopensc/card.c @@ -767,3 +767,16 @@ inline int sc_card_valid(const struct sc_card *card) { #endif return card->magic == SC_CARD_MAGIC; } + +int +sc_card_ctl(struct sc_card *card, unsigned long cmd, void *args) +{ + int r; + + assert(card != NULL); + SC_FUNC_CALLED(card->ctx, 2); + if (card->ops->card_ctl == NULL) + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_SUPPORTED); + r = card->ops->card_ctl(card, cmd, args); + SC_FUNC_RETURN(card->ctx, 2, r); +} diff --git a/src/libopensc/opensc-pkcs15.h b/src/libopensc/opensc-pkcs15.h index 3df6e159..8bcf2110 100644 --- a/src/libopensc/opensc-pkcs15.h +++ b/src/libopensc/opensc-pkcs15.h @@ -285,6 +285,15 @@ int sc_pkcs15_find_pin_by_auth_id(struct sc_pkcs15_card *card, const struct sc_pkcs15_id *id, struct sc_pkcs15_pin_info **out); +int sc_pkcs15_encode_dir(struct sc_context *ctx, + struct sc_pkcs15_card *card, + u8 **buf, size_t *buflen); +int sc_pkcs15_encode_tokeninfo(struct sc_context *ctx, + struct sc_pkcs15_card *card, + u8 **buf, size_t *buflen); +int sc_pkcs15_encode_odf(struct sc_context *ctx, + struct sc_pkcs15_card *card, + u8 **buf, size_t *buflen); int sc_pkcs15_encode_df(struct sc_context *ctx, struct sc_pkcs15_df *df, int file_nr, u8 **buf, size_t *bufsize); diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index 87b93312..3dfec4fb 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -316,7 +316,7 @@ struct sc_card_operations { /* Called when the card object is being freed. finish() has to * deallocate all possible private data. */ int (*finish)(struct sc_card *card); - + /* ISO 7816-4 functions */ int (*read_binary)(struct sc_card *card, unsigned int idx, @@ -389,6 +389,8 @@ struct sc_card_operations { int (*list_files)(struct sc_card *card, u8 *buf, size_t buflen); int (*check_sw)(struct sc_card *card, int sw1, int sw2); + int (*card_ctl)(struct sc_card *card, unsigned long request, + void *data); }; struct sc_card_driver { @@ -567,6 +569,9 @@ int sc_reset_retry_counter(struct sc_card *card, unsigned int type, int sc_create_file(struct sc_card *card, struct sc_file *file); int sc_delete_file(struct sc_card *card, const struct sc_path *path); +/* Card controls */ +int sc_card_ctl(struct sc_card *card, unsigned long command, void *arg); + inline int sc_file_valid(const struct sc_file *file); struct sc_file * sc_file_new(); void sc_file_free(struct sc_file *file); diff --git a/src/libopensc/pkcs15.c b/src/libopensc/pkcs15.c index 06b710ae..0a2cdec6 100644 --- a/src/libopensc/pkcs15.c +++ b/src/libopensc/pkcs15.c @@ -359,7 +359,7 @@ static int parse_odf(const u8 * buf, int buflen, struct sc_pkcs15_card *card) return 0; } -static int sc_pkcs15_encode_odf(struct sc_context *ctx, +int sc_pkcs15_encode_odf(struct sc_context *ctx, struct sc_pkcs15_card *card, u8 **buf, size_t *buflen) { diff --git a/src/libopensc/pkcs15.h b/src/libopensc/pkcs15.h index 3df6e159..8bcf2110 100644 --- a/src/libopensc/pkcs15.h +++ b/src/libopensc/pkcs15.h @@ -285,6 +285,15 @@ int sc_pkcs15_find_pin_by_auth_id(struct sc_pkcs15_card *card, const struct sc_pkcs15_id *id, struct sc_pkcs15_pin_info **out); +int sc_pkcs15_encode_dir(struct sc_context *ctx, + struct sc_pkcs15_card *card, + u8 **buf, size_t *buflen); +int sc_pkcs15_encode_tokeninfo(struct sc_context *ctx, + struct sc_pkcs15_card *card, + u8 **buf, size_t *buflen); +int sc_pkcs15_encode_odf(struct sc_context *ctx, + struct sc_pkcs15_card *card, + u8 **buf, size_t *buflen); int sc_pkcs15_encode_df(struct sc_context *ctx, struct sc_pkcs15_df *df, int file_nr, u8 **buf, size_t *bufsize); diff --git a/src/libopensc/sc.c b/src/libopensc/sc.c index 2c8fbd56..87ca8b41 100644 --- a/src/libopensc/sc.c +++ b/src/libopensc/sc.c @@ -190,7 +190,7 @@ int sc_establish_context(struct sc_context **ctx_out) #if 1 ctx->card_drivers[i++] = sc_get_tcos_driver(); #endif -#if 0 && defined(HAVE_OPENSSL) +#if defined(HAVE_OPENSSL) ctx->card_drivers[i++] = sc_get_gpk_driver(); #endif #if 1 @@ -368,6 +368,15 @@ int sc_file_add_acl_entry(struct sc_file *file, unsigned int operation, sc_file_clear_acl_entries(file, operation); file->acl[operation] = (struct sc_acl_entry *) 3; return 0; + default: + /* NONE and UNKNOWN get zapped when a new AC is added. + * If the ACL is NEVER, additional entries will be + * dropped silently. */ + if (file->acl[operation] == (struct sc_acl_entry *) 1) + return 0; + if (file->acl[operation] == (struct sc_acl_entry *) 2 + || file->acl[operation] == (struct sc_acl_entry *) 3) + file->acl[operation] = NULL; } new = malloc(sizeof(struct sc_acl_entry)); diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index 6e4d0b0b..bd41c539 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -5,7 +5,7 @@ LDFLAGS = @LDFLAGS@ @LIBOPENSC@ if HAVE_SSL bin_PROGRAMS = opensc-tool opensc-explorer pkcs15-crypt pkcs15-tool \ - cryptoflex-tool + cryptoflex-tool pkcs15-init else bin_PROGRAMS = opensc-tool opensc-explorer pkcs15-crypt pkcs15-tool endif @@ -21,4 +21,7 @@ pkcs15_crypt_SOURCES = pkcs15-crypt.c util.c pkcs15_crypt_LDADD = @GETOPTSRC@ cryptoflex_tool_SOURCES = cryptoflex-tool.c util.c cryptoflex_tool_LDADD = @GETOPTSRC@ @LIBCRYPTO@ -noinst_HEADERS = util.h +pkcs15_init_SOURCES = pkcs15-init.c profile.c util.c \ + pkcs15-gpk.c +pkcs15_init_LDADD = @GETOPTSRC@ @LIBCRYPTO@ +noinst_HEADERS = util.h profile.h diff --git a/src/tools/opensc-explorer.c b/src/tools/opensc-explorer.c index ee91a283..dcad5a09 100644 --- a/src/tools/opensc-explorer.c +++ b/src/tools/opensc-explorer.c @@ -51,21 +51,12 @@ const char *option_help[] = { "Debug output -- maybe supplied several times", }; -#define CMD_LS 0 -#define CMD_CD 1 -#define CMD_DEBUG 2 -#define CMD_CAT 3 -#define CMD_INFO 4 -#define CMD_DELETE 5 -#define CMD_VERIFY 6 - -const char *cmds[] = { - "ls", "cd", "debug", "cat", "info", "create", "delete", - "verify", "put", "get", "mkdir", "quit" +struct command { + const char * name; + int (*func)(const char *, const char *); + const char * help; }; -const int nr_cmds = sizeof(cmds)/sizeof(cmds[0]); - void die(int ret) { if (current_file != NULL) @@ -79,22 +70,22 @@ void die(int ret) exit(ret); } -static int ambiguous_match(const char **table, int nr_entries, const char *cmd) +static struct command * +ambiguous_match(struct command *table, const char *cmd) { + struct command *last_match = NULL; int matches = 0; - int last_match = 0; - int i; - for (i = 0; i < nr_entries; i++) { - if (strncasecmp(cmd, table[i], strlen(cmd)) == 0) { + for (; table->name; table++) { + if (strncasecmp(cmd, table->name, strlen(cmd)) == 0) { + last_match = table; matches++; - last_match = i; } } - if (matches > 1) - return -1; - if (matches == 0) - return -2; + if (matches > 1) { + printf("Ambiguous command: %s\n", cmd); + return NULL; + } return last_match; } @@ -172,7 +163,7 @@ void print_file(const struct sc_file *file) return; } -int do_ls() +int do_ls(const char *dummy, const char *dummy2) { u8 buf[256], *cur = buf; int r, count; @@ -209,7 +200,7 @@ int do_ls() return 0; } -int do_cd(const char *arg) +int do_cd(const char *arg, const char *dummy2) { struct sc_path path; struct sc_file *file; @@ -305,7 +296,7 @@ int read_and_print_record_file(struct sc_file *file) return 0; } -int do_cat(const char *arg) +int do_cat(const char *arg, const char *dummy2) { int r, error = 0; struct sc_path path; @@ -342,7 +333,7 @@ int do_cat(const char *arg) return -error; } -int do_info(const char *arg) +int do_info(const char *arg, const char *dummy2) { struct sc_file *file; struct sc_path path; @@ -517,7 +508,7 @@ usage: return -1; } -int do_delete(const char *arg) +int do_delete(const char *arg, const char *dummy2) { struct sc_path path; int r; @@ -739,59 +730,53 @@ usage: return -1; } -int handle_cmd(int cmd, const char *arg, const char *arg2) +int do_debug(const char *arg, const char *dummy2) { - int i; + int i; - switch (cmd) { - case 0: - return do_ls(); - case 1: - return do_cd(arg); - case 2: - if (sscanf(arg, "%d", &i) != 1) - return -1; - printf("Debug level set to %d\n", i); - ctx->debug = i; - if (i) { - ctx->error_file = stderr; - ctx->debug_file = stdout; - } else { - ctx->error_file = NULL; - ctx->debug_file = NULL; - } - return 0; - case 3: - return do_cat(arg); - case 4: - return do_info(arg); - case 5: - return do_create(arg, arg2); - case 6: - return do_delete(arg); - case 7: - return do_verify(arg, arg2); - case 8: - return do_put(arg, arg2); - case 9: - return do_get(arg, arg2); - case 10: - return do_mkdir(arg, arg2); - case 11: - die(0); - default: - printf("Don't know how to handle command.\n"); + if (sscanf(arg, "%d", &i) != 1) + return -1; + printf("Debug level set to %d\n", i); + ctx->debug = i; + if (i) { + ctx->error_file = stderr; + ctx->debug_file = stdout; + } else { + ctx->error_file = NULL; + ctx->debug_file = NULL; } - return -1; + return 0; } +int do_quit(const char *dummy, const char *dummy2) +{ + die(0); + return 0; +} + +struct command cmds[] = { + { "ls", do_ls, "list all files in the current DF" }, + { "cd", do_cd, "change to another DF" }, + { "debug", do_debug, "set the debug level" }, + { "cat", do_cat, "print the contents of an EF" }, + { "info", do_info, "display attributes of card file" }, + { "create", do_create, "create a new EF" }, + { "delete", do_delete, "remove an EF/DF" }, + { "verify", do_verify, "present a PIN or key to the card" }, + { "put", do_put, "copy a local file to the card" }, + { "get", do_get, "copy an EF to a local file" }, + { "mkdir", do_mkdir, "create a DF" }, + { "quit", do_quit, "quit this program" }, + { 0, 0, 0 } +}; + void usage() { - int i; - + struct command *cmd; + printf("Supported commands:\n"); - for (i = 0; i < nr_cmds; i++) - printf(" %s\n", cmds[i]); + for (cmd = cmds; cmd->name; cmd++) + printf(" %-10s %s\n", cmd->name, cmd->help); } static int parse_line(char *in, char **argv) @@ -911,6 +896,7 @@ int main(int argc, char * const argv[]) return 1; } while (1) { + struct command *c; int i; char prompt[40]; @@ -929,12 +915,12 @@ int main(int argc, char * const argv[]) continue; while (r < 3) cargv[r++] = ""; - r = ambiguous_match(cmds, nr_cmds, cargv[0]); - if (r < 0) { + c = ambiguous_match(cmds, cargv[0]); + if (c == NULL) { usage(); - continue; + } else { + c->func(cargv[1], cargv[2]); } - handle_cmd(r, cargv[1], cargv[2]); } end: die(err); diff --git a/src/tools/util.c b/src/tools/util.c index 4b8f0cbc..713df0da 100644 --- a/src/tools/util.c +++ b/src/tools/util.c @@ -3,6 +3,7 @@ #endif #include #include +#include #include #include "util.h" @@ -139,3 +140,40 @@ const char * acl_to_str(const struct sc_acl_entry *e) line[strlen(line)-1] = 0; /* get rid of trailing space */ return line; } + +void +fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\nAborting.\n"); + va_end(ap); + exit(1); +} + +void +error(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +void +warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "warning: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} diff --git a/src/tools/util.h b/src/tools/util.h index aff53ba1..3beeaf25 100644 --- a/src/tools/util.h +++ b/src/tools/util.h @@ -26,5 +26,8 @@ void hex_dump(FILE *f, const u8 *in, int len, const char *sep); void hex_dump_asc(FILE *f, const u8 *in, size_t count, int addr); void print_usage_and_die(const char *pgmname); const char * acl_to_str(const struct sc_acl_entry *e); +void warn(const char *fmt, ...); +void error(const char *fmt, ...); +void fatal(const char *fmt, ...); #endif