/* * Copyright (C) 2018 Frank Morgner * * This file is part of OpenSC. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "fread_to_eof.h" #include #include "goid-tool-cmdline.h" #include "libopensc/asn1.h" #include "libopensc/log.h" #include "libopensc/opensc.h" #include "sm/sm-eac.h" #ifdef ENABLE_OPENPACE #include #endif #include #include "util.h" #include const unsigned char aid_soc_manager[] = { 0xD2, 0x76, 0x00, 0x01, 0x72, 0x53, 0x6F, 0x43, 0x4D, 0x01 }; static const unsigned char paccess_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x72, 0x50, 0x41, 0x63, 0x63, 0x01, }; static const char *app_name = "goid-tool"; #define SOCM_AUTHOBJECT_PIN 0x80 #define SOCM_AUTHOBJECT_BIO 0x40 #define SOCM_AUTHOBJECT_GP 0x20 void print_permissions(u8 permissions) { size_t perms_printed = 0; if (permissions & SOCM_AUTHOBJECT_PIN) { printf("verification of PIN"); perms_printed++; } if (permissions & SOCM_AUTHOBJECT_BIO) { printf("%s BIO", perms_printed ? " or" : "verification of"); perms_printed++; } if (permissions & SOCM_AUTHOBJECT_GP) { printf("%s GP key", perms_printed ? " or" : "verification of"); perms_printed++; } printf("\n"); } void soc_info(sc_context_t *ctx, sc_card_t *card) { sc_apdu_t apdu; unsigned char rbuf[SC_MAX_APDU_RESP_SIZE]; u8 information_applets[SC_MAX_APDU_RESP_SIZE]; size_t information_applets_len = sizeof information_applets; int pin_initialized = 0, bio_initialized = 0; int pin_max_retries = 0, pin_cur_retries = 0, bio_max_retries = 0, bio_cur_retries = 0; int pin_length = 0; u8 pin_unblock = 0, pin_change = 0, bio_unblock = 0, bio_change = 0; size_t pin_change_len = sizeof pin_change, pin_unblock_len = sizeof pin_unblock, bio_change_len = sizeof bio_change, bio_unblock_len = sizeof bio_unblock; int bio_count = 0; u8 bio_initialized_templates[2]; size_t bio_initialized_templates_len = sizeof bio_initialized_templates; struct sc_asn1_entry rapdu_get_information[] = { { "Sequence of (applet register)", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_SEQUENCE|SC_ASN1_CONS, 0, NULL, NULL }, { "Initialized PIN", SC_ASN1_STRUCT, SC_ASN1_APP|SC_ASN1_CONS|0x02, SC_ASN1_OPTIONAL, NULL, NULL }, { "Initialized BIO", SC_ASN1_STRUCT, SC_ASN1_APP|SC_ASN1_CONS|0x03, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; struct sc_asn1_entry rapdu_get_information_pin[] = { { "Initialization state of the PIN", SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, 0, NULL, NULL }, { "maximum remaining tries", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "current remaining tries", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "Unblock requirements Mask", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x1, 0, NULL, NULL }, { "Change requirements Mask", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x2, 0, NULL, NULL }, { "PIN size", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; struct sc_asn1_entry rapdu_get_information_bio[] = { { "Initialization state of the BIO", SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, 0, NULL, NULL }, { "maximum remaining tries", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "current remaining tries", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "Unblock requirements Mask", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x1, 0, NULL, NULL }, { "Change requirements Mask", SC_ASN1_OCTET_STRING, SC_ASN1_APP|0x2, 0, NULL, NULL }, { "Min minutiae", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "Max minutiae", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "number of templates", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, { "Bitmap of initialized templates", SC_ASN1_BIT_STRING, SC_ASN1_TAG_BIT_STRING, 0, NULL, NULL }, { "Algorithm parameters, allocation strategy", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, 0, NULL, NULL }, { NULL , 0 , 0 , 0 , NULL , NULL } }; sc_format_apdu_ex(&apdu, 0x00, 0x61, 0x00, 0x00, NULL, 0, rbuf, sizeof rbuf); apdu.cla = 0x80; if (sc_transmit_apdu(card, &apdu) != SC_SUCCESS) { return; } sc_format_asn1_entry(rapdu_get_information + 0, information_applets, &information_applets_len, 0); sc_format_asn1_entry(rapdu_get_information + 1, rapdu_get_information_pin, NULL, 0); sc_format_asn1_entry(rapdu_get_information + 2, rapdu_get_information_bio, NULL, 0); sc_format_asn1_entry(rapdu_get_information_pin + 0, &pin_initialized, NULL, 0); sc_format_asn1_entry(rapdu_get_information_pin + 1, &pin_max_retries, NULL, 0); sc_format_asn1_entry(rapdu_get_information_pin + 2, &pin_cur_retries, NULL, 0); sc_format_asn1_entry(rapdu_get_information_pin + 3, &pin_unblock, &pin_unblock_len, 0); sc_format_asn1_entry(rapdu_get_information_pin + 4, &pin_change, &pin_change_len, 0); sc_format_asn1_entry(rapdu_get_information_pin + 5, &pin_length, NULL, 0); sc_format_asn1_entry(rapdu_get_information_bio + 0, &bio_initialized, NULL, 0); sc_format_asn1_entry(rapdu_get_information_bio + 1, &bio_max_retries, NULL, 0); sc_format_asn1_entry(rapdu_get_information_bio + 2, &bio_cur_retries, NULL, 0); sc_format_asn1_entry(rapdu_get_information_bio + 3, &bio_unblock, &bio_unblock_len, 0); sc_format_asn1_entry(rapdu_get_information_bio + 4, &bio_change, &bio_change_len, 0); sc_format_asn1_entry(rapdu_get_information_bio + 7, &bio_count, NULL, 0); sc_format_asn1_entry(rapdu_get_information_bio + 8, bio_initialized_templates, &bio_initialized_templates_len, 0); if (sc_asn1_decode(ctx, rapdu_get_information, apdu.resp, apdu.resplen, NULL, NULL) != SC_SUCCESS) { return; } if (rapdu_get_information[0].flags & SC_ASN1_PRESENT && information_applets_len > 0) { const unsigned char *p = information_applets, *end = information_applets + information_applets_len; unsigned int cla = 0, tag = 0; size_t length = information_applets_len; if (SC_SUCCESS == sc_asn1_read_tag(&p, length, &cla, &tag, &length) && cla == SC_ASN1_TAG_UNIVERSAL && tag == SC_ASN1_TAG_INTEGER) { int applet_count = 0; /* number of applets */ if (SC_SUCCESS == sc_asn1_decode_integer(p, length, &applet_count, 0)) { printf("SoCManager knows %d applet%s%s\n", applet_count, applet_count == 1 ? "" : "s", applet_count == 0 ? "" : ":"); /* AID of client applet #x */ for (p += length, length = end - p; p < end; p += length, length = end - p) { size_t i; if (SC_SUCCESS != sc_asn1_read_tag(&p, length, &cla, &tag, &length) || p == NULL || cla != SC_ASN1_TAG_CONTEXT) { break; } putchar('\t'); util_hex_dump(stdout, p, length, ""); /* align with the maximum length of an AID */ for (i = length; i < 0x10 + 1; i++) printf(" "); /* i now counts the number of flags that were printed */ i = 0; if (tag & 0x02) { printf("default selected"); i++; } if (tag & 0x01) { printf("%sinteracts with SoCManager", i ? ", " : ""); i++; } if (tag & 0x04) { printf("%sBIO enabled", i ? ", " : ""); i++; } if (tag & 0x08) { printf("%sPIN enabled", i ? ", " : ""); i++; } printf("\n"); } } } } if (rapdu_get_information[1].flags & SC_ASN1_PRESENT) { if (pin_initialized) { printf("PIN is initialized with %d digits (%d of %d tries left).\n", pin_length, pin_cur_retries, pin_max_retries); } else { printf("PIN is not initialized.\n"); } printf("\tChanging PIN requires "); print_permissions(pin_change); printf("\tUnblocking PIN requires "); print_permissions(pin_unblock); } if (rapdu_get_information[2].flags & SC_ASN1_PRESENT) { if (bio_initialized) { int bio_used = 0; size_t i, j; for (i = 0; i < sizeof bio_initialized_templates; i++) { for (j = 0; j < 8; j++) { if (bio_initialized_templates[i] >> j & 0x1) bio_used++; } } printf("BIO is initialized with %d of %d templates (%d of %d tries left).\n", bio_used, bio_count, bio_cur_retries, bio_max_retries); } else { printf("BIO is not initialized.\n"); } printf("\tChanging BIO requires "); print_permissions(bio_change); printf("\tUnblocking BIO requires "); print_permissions(bio_unblock); } } int soc_verify(sc_card_t *card, unsigned char p2) { int ok = 0; sc_apdu_t apdu; sc_format_apdu_ex(&apdu, 0x00, 0x20, 0x00, p2, NULL, 0, NULL, 0); SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, sc_transmit_apdu(card, &apdu), "Verification failed"); SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, sc_check_sw(card, apdu.sw1, apdu.sw2), "Verification failed"); ok = 1; err: return ok; } int soc_reset(sc_card_t *card, unsigned char p2) { int ok = 0; sc_apdu_t apdu; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0xFF, p2); SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, sc_transmit_apdu(card, &apdu), "Reset failed"); if (apdu.sw1 != 0x63) { SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, sc_check_sw(card, apdu.sw1, apdu.sw2), "Reset failed"); } ok = 1; err: return ok; } int soc_change(sc_card_t *card, unsigned char p1, unsigned char p2) { int ok = 0; sc_apdu_t apdu; sc_format_apdu_ex(&apdu, 0x00, 0x24, p1, p2, NULL, 0, NULL, 0); SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, sc_transmit_apdu(card, &apdu), "Changing secret failed"); while (apdu.sw1 == 0x91 && apdu.sw2 == 0x00) { switch (p2) { case SOCM_AUTHOBJECT_PIN: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Verify your PIN on the card using the same position."); break; case SOCM_AUTHOBJECT_BIO: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Verify your finger print on the card using the same position."); break; } SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, sc_transmit_apdu(card, &apdu), "Changing secret failed"); } SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, sc_check_sw(card, apdu.sw1, apdu.sw2), "Verification failed"); ok = 1; err: return ok; } int soc_main(struct sc_context *ctx, sc_card_t *card, struct gengetopt_args_info *cmdline) { int ok = 0; sc_file_t *file = NULL; struct sc_path path; unsigned char soc_manager_minor = 0; unsigned char soc_manager_major = 0; unsigned char soc_reset_authobject = 0; sc_path_set(&path, SC_PATH_TYPE_DF_NAME, aid_soc_manager, sizeof aid_soc_manager, 0, 0); SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, sc_select_file(card, &path, &file), "SoCManager not found."); if (file && file->prop_attr && file->prop_attr_len) { size_t prop_len = 0; const u8 *prop = sc_asn1_find_tag(ctx, file->prop_attr, file->prop_attr_len, 0x81, &prop_len); if (prop && prop_len == 2) { soc_manager_major = prop[0]; soc_manager_minor = prop[1]; sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "SoCManager version %u.%u", soc_manager_major, soc_manager_minor); } } if (cmdline->info_given) { if ((soc_manager_major == 2 && soc_manager_minor < 7) || soc_manager_major < 2) SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_NOT_SUPPORTED, "Get Information only supported with version 2.07 and later."); soc_info(ctx, card); } if (cmdline->verify_pin_given) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Verify PIN on the card."); if (!soc_verify(card, SOCM_AUTHOBJECT_PIN)) goto err; soc_reset_authobject |= SOCM_AUTHOBJECT_PIN; } if (cmdline->verify_bio_given) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Verify finger print on the card."); if (!soc_verify(card, SOCM_AUTHOBJECT_BIO)) goto err; soc_reset_authobject |= SOCM_AUTHOBJECT_BIO; } if (cmdline->verify_pin_or_bio_given) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Verify finger print or PIN on the card."); if (!soc_verify(card, SOCM_AUTHOBJECT_PIN|SOCM_AUTHOBJECT_BIO)) goto err; soc_reset_authobject |= SOCM_AUTHOBJECT_PIN|SOCM_AUTHOBJECT_BIO; } if (cmdline->new_pin_given) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Initialize the PIN on the card."); if (!soc_change(card, 0x00, SOCM_AUTHOBJECT_BIO)) goto err; } if (cmdline->new_bio_given) { size_t i = 0; while (i < cmdline->new_bio_given) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Initialize finger print template %u on the card.", (unsigned char) i); if (!soc_change(card, (unsigned char) i, SOCM_AUTHOBJECT_BIO)) goto err; i++; } } ok = 1; err: if (soc_reset_authobject) soc_reset(card, soc_reset_authobject); return ok; } static int paccess_construct_fci(struct sc_card *card, const sc_file_t *file, u8 *out, size_t *outlen) { u8 *p = out; u8 buf[64]; if (*outlen < 2) return SC_ERROR_BUFFER_TOO_SMALL; *p++ = 0x62; p++; buf[0] = (file->size >> 8) & 0xFF; buf[1] = file->size & 0xFF; sc_asn1_put_tag(0x80, buf, 2, p, *outlen - (p - out), &p); buf[0] = (file->id >> 8) & 0xFF; buf[1] = file->id & 0xFF; sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p); memcpy(buf, file->sec_attr, file->sec_attr_len); sc_asn1_put_tag(0x86, buf, file->sec_attr_len, p, *outlen - (p - out), &p); buf[0] = file->sid & 0xFF; sc_asn1_put_tag(0x88, buf, 1, p, *outlen - (p - out), &p); out[1] = p - out - 2; *outlen = p - out; return 0; } int paccess_create_file(struct sc_card *card, size_t size, int fid, u8 *sec_attr, size_t sec_attr_len, int sfid) { int ok = 0; sc_file_t *file = sc_file_new(); if (!file) goto err; file->size = size; file->id = fid; file->sid = sfid; file->sec_attr = sec_attr; file->sec_attr_len = sec_attr_len; card->ops->construct_fci = paccess_construct_fci; SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, sc_create_file(card, file), "Create file failed."); ok = 1; err: return ok; } int paccess_delete_file(struct sc_card *card, int fid) { int ok = 0; u8 buf[2]; struct sc_path path; buf[0] = (fid >> 8) & 0xFF; buf[1] = fid & 0xFF; sc_path_set(&path, SC_PATH_TYPE_FILE_ID, buf, sizeof buf, 0, 0); SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, sc_delete_file(card, &path), "Delete file failed."); ok = 1; err: return ok; } int paccess_get_security_attributes(struct sc_context *ctx, const char *ac, int* chatbits, size_t chatbits_len, u8 sec_attr[2]) { int ok = 0; memset(sec_attr, 0, 2); if (!ac || 0 == strcmp(ac, "never")) { sec_attr[0] |= 0xFF; } else if (0 == strcmp(ac, "always")) { /* nothing else to do */ } else { size_t i; if (0 == strcmp(ac, "ta")) { sec_attr[0] |= 0xA0; } else if (0 == strcmp(ac, "sm")) { sec_attr[0] |= 0xC0; } else { SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS, "unknown access condition."); } for (i = 0; i < chatbits_len; i++) { u8 byte = chatbits[i] / 8; u8 bit = chatbits[i] % 8 + 1; if (byte > 5) SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS, "Only CHAT bits with index 0..39 are available."); sec_attr[0] |= 0x8 | byte; sec_attr[1] |= bit; } } ok = 1; err: return ok; } #define PXS_AUTHOBJECT_PIN 0x80 #define PXS_AUTHOBJECT_BIO 0x40 int paccess_verify(sc_card_t *card, unsigned char p2) { int ok = 0; sc_apdu_t apdu; sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2E, 0x24, p2); SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, sc_transmit_apdu(card, &apdu), "Verification failed"); SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL, sc_check_sw(card, apdu.sw1, apdu.sw2), "Verification failed"); ok = 1; err: return ok; } int paccess_main(struct sc_context *ctx, sc_card_t *card, struct gengetopt_args_info *cmdline) { int ok = 0, r; sc_file_t *file = NULL; struct sc_path path; size_t i, ef_cardsecurity_len = 0, privkey_len = 0, *certs_lens = NULL; unsigned char *ef_cardsecurity = NULL, *privkey = NULL, **certs = NULL; unsigned char auxiliary_data[] = {0x67, 0x00}; unsigned char paccess_minor = 0; unsigned char paccess_major = 0; int pxs_reset_authobjects = 0; sc_path_set(&path, SC_PATH_TYPE_DF_NAME, paccess_aid, sizeof paccess_aid, 0, 0); SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, sc_select_file(card, &path, &file), "PAccess not found."); if (file && file->prop_attr && file->prop_attr_len) { const unsigned char *p, *end; unsigned int cla = 0, tag = 0; size_t length; for (p = file->prop_attr, length = file->prop_attr_len, end = file->prop_attr + file->prop_attr_len; p < end; p += length, length = end - p) { if (SC_SUCCESS != sc_asn1_read_tag(&p, length, &cla, &tag, &length) || p == NULL) { break; } switch (cla | tag) { case 0x81: if (p && length == 2) { paccess_major = p[0]; paccess_minor = p[1]; sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "PAccess version %u.%u", paccess_major, paccess_minor); } break; case 0x82: if (p && length == 1) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Number of Session Contexts %u", p[0]); } break; case 0x87: sc_debug_hex(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Certificate Authority Reference of the primary CVCA trust anchor", p, length); break; case 0x88: sc_debug_hex(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Certificate Authority Reference of the secondary CVCA trust anchor", p, length); break; case 0x1fe5: case 0x9F65: if (p && length == 2) { size_t max_command_size = (p[0]<<8)|p[1]; card->max_recv_size = max_command_size; card->max_send_size = max_command_size; sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Maximum data length in command message %"SC_FORMAT_LEN_SIZE_T"u bytes", max_command_size); } break; } } } if (cmdline->certificate_given || cmdline->key_given) { if (!fread_to_eof(cmdline->key_arg, &privkey, &privkey_len)) { SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS, "Could not parse private key.\n"); } certs = calloc(sizeof *certs, cmdline->certificate_given + 1); certs_lens = calloc(sizeof *certs_lens, cmdline->certificate_given + 1); if (!certs || !certs_lens) { SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_NOT_ENOUGH_MEMORY, "Internal error."); } for (i = 0; i < cmdline->certificate_given; i++) { if (!fread_to_eof(cmdline->certificate_arg[i], (unsigned char **) &certs[i], &certs_lens[i])) { SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS, "Could not read certificate.\n"); } } #ifdef ENABLE_OPENPACE EAC_init(); #endif SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, perform_terminal_authentication(card, (const unsigned char **) certs, certs_lens, privkey, privkey_len, auxiliary_data, sizeof auxiliary_data), "Terminal authentication failed."); SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, perform_chip_authentication(card, &ef_cardsecurity, &ef_cardsecurity_len), "Chip authentication failed."); } if (cmdline->verify_pin_given) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Verify PIN on the card."); if (!paccess_verify(card, PXS_AUTHOBJECT_PIN)) goto err; pxs_reset_authobjects++; } if (cmdline->verify_bio_given) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Verify finger print on the card."); if (!paccess_verify(card, PXS_AUTHOBJECT_BIO)) goto err; pxs_reset_authobjects++; } if (cmdline->verify_pin_or_bio_given) { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Verify finger print or PIN on the card."); if (!paccess_verify(card, PXS_AUTHOBJECT_PIN|PXS_AUTHOBJECT_BIO)) goto err; pxs_reset_authobjects++; } for (i = 0; i < cmdline->delete_dg_given; i++) { int fid = 0x0100 | cmdline->delete_dg_arg[i]; if ((paccess_major == 2 && paccess_minor < 6) || paccess_major < 2) SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_NOT_SUPPORTED, "Create File only supported with version 2.06 and later."); if (!paccess_delete_file(card, fid)) goto err; } for (i = 0; i < cmdline->create_dg_given; i++) { u8 sec_attr[4]; int fid = 0x0100 | cmdline->create_dg_arg[i]; if ((paccess_major == 2 && paccess_minor < 6) || paccess_major < 2) SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_NOT_SUPPORTED, "Create File only supported with version 2.06 and later."); if (cmdline->new_size_arg < 0) SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS, "`--new-size' needs a positive size.\n"); if (!paccess_get_security_attributes(ctx, cmdline->new_read_ac_arg, cmdline->new_read_ac_chatbit_arg, cmdline->new_read_ac_chatbit_given, sec_attr + 0) || !paccess_get_security_attributes(ctx, cmdline->new_write_ac_arg, cmdline->new_write_ac_chatbit_arg, cmdline->new_write_ac_chatbit_given, sec_attr + 2) || !paccess_create_file(card, cmdline->new_size_arg, fid, sec_attr, sizeof sec_attr, cmdline->create_dg_arg[i])) goto err; } if (cmdline->out_file_given > 0 && cmdline->out_file_given != cmdline->read_dg_given) { SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS, "If `--out-file' is specified, it must be used as many times as `--read-dg'.\n"); } for (i = 0; i < cmdline->read_dg_given; i++) { u8 *ef = NULL; size_t ef_len = 0; r = iso7816_read_binary_sfid(card, cmdline->read_dg_arg[i], &ef, &ef_len); if (r >= 0) { if (cmdline->out_file_given == cmdline->read_dg_given) { FILE *f = fopen(cmdline->out_file_arg[i], "wb"); if (f) { fwrite(ef, ef_len, 1, f); fclose(f); } else { sc_debug(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, "Error opening %s: %s\n", cmdline->out_file_arg[i], strerror(errno)); r = SC_ERROR_FILE_NOT_FOUND; } } else { char label[32]; snprintf(label, sizeof label, "Data Group %u", (unsigned char) cmdline->read_dg_arg[i]); sc_debug_hex(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, label, ef, ef_len); } free(ef); } SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, "Error reading data group."); } if (cmdline->print_cardid_given) { u8 *ef = NULL; size_t ef_len = 0; r = iso7816_read_binary_sfid(card, 0x1E, &ef, &ef_len); if (r >= 0) { const u8 *p = ef; unsigned int cla = 0, tag = 0; if (SC_SUCCESS == sc_asn1_read_tag(&p, ef_len, &cla, &tag, &ef_len) && (tag | cla) == 0x7E && SC_SUCCESS == sc_asn1_read_tag(&p, ef_len, &cla, &tag, &ef_len) && (tag | cla) == 0x13) { const unsigned char *cardid = (const unsigned char *) p; while (cardid && ef_len) { if (isprint(*cardid)) { printf("%c", *cardid); } else { printf("."); } cardid++; ef_len--; } if (cardid) printf("\n"); } else { r = SC_ERROR_INVALID_DATA; } } free(ef); SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, "Error reading card ID."); } if (cmdline->print_paccessid_given) { u8 *ef = NULL; size_t ef_len = 0; r = iso7816_read_binary_sfid(card, 0x06, &ef, &ef_len); if (r >= 0) { const u8 *p = ef; unsigned int cla = 0, tag = 0; if (SC_SUCCESS == sc_asn1_read_tag(&p, ef_len, &cla, &tag, &ef_len) && (tag | cla) == 0x66 && SC_SUCCESS == sc_asn1_read_tag((const u8 **) &p, ef_len, &cla, &tag, &ef_len) && (tag | cla) == 0x13) { const unsigned char *paccessid = (const unsigned char *) p; while (paccessid && ef_len) { if (isprint(*paccessid)) { printf("%c", *paccessid); } else { printf("."); } paccessid++; ef_len--; } if (paccessid) printf("\n"); } else { r = SC_ERROR_INVALID_DATA; } } free(ef); SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, "Error reading card ID."); } if (cmdline->in_file_given != cmdline->write_dg_given) { SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS, "If `--in-file' is specified, it must be used as many times as `--write-dg'.\n"); } for (i = 0; i < cmdline->write_dg_given; i++) { u8 *ef = NULL; size_t ef_len = 0; if (!fread_to_eof(cmdline->in_file_arg[i], &ef, &ef_len)) { SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS, "Could not read input file.\n"); } r = iso7816_update_binary_sfid(card, cmdline->write_dg_arg[i], ef, ef_len); free(ef); SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, "Error writing data group."); } if (cmdline->write_cardid_arg) { size_t cardid_len = strlen(cmdline->write_cardid_arg); u8 ef[256]; if (cardid_len > (sizeof ef) - 4) { SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS, "Card ID too long."); } ef[0] = 0x7E; ef[1] = 2 + cardid_len; ef[2] = 0x13; ef[3] = cardid_len; memcpy(ef + 4, cmdline->write_cardid_arg, cardid_len); r = iso7816_update_binary_sfid(card, 0x1E, ef, 4 + cardid_len); SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, "Error writing card ID."); } if (cmdline->write_paccessid_arg) { size_t paccessid_len = strlen(cmdline->write_paccessid_arg); u8 ef[256]; if (paccessid_len > (sizeof ef) - 4) { SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, SC_ERROR_INVALID_ARGUMENTS, "Card ID too long."); } ef[0] = 0x66; ef[1] = 2 + paccessid_len; ef[2] = 0x13; ef[3] = paccessid_len; memcpy(ef + 4, cmdline->write_paccessid_arg, paccessid_len); r = iso7816_update_binary_sfid(card, 0x06, ef, 4 + paccessid_len); SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, "Error writing PAccess ID."); } ok = 1; err: if (pxs_reset_authobjects) sc_reset(card, 0); if (certs) { for (i = 0; certs[i]; i++) { free((unsigned char *) certs[i]); } free(certs); } free(ef_cardsecurity); free(certs_lens); free(privkey); sc_file_free(file); return ok; } int main(int argc, char **argv) { struct gengetopt_args_info cmdline; struct sc_context *ctx = NULL; struct sc_card *card = NULL; int r, fail = 1; sc_context_param_t ctx_param; if (cmdline_parser(argc, argv, &cmdline) != 0) exit(1); memset(&ctx_param, 0, sizeof(ctx_param)); ctx_param.ver = 0; ctx_param.app_name = app_name; r = sc_context_create(&ctx, &ctx_param); if (r) { fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r)); exit(1); } if (cmdline.verbose_given > 1) { ctx->debug = cmdline.verbose_given; sc_ctx_log_to_file(ctx, "stderr"); } r = sc_set_card_driver(ctx, "default"); SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, "Error selecting card driver."); r = util_connect_card_ex(ctx, &card, cmdline.reader_arg, 0, 0, cmdline.verbose_given); SC_TEST_GOTO_ERR(ctx, SC_LOG_DEBUG_VERBOSE_TOOL, r, "Error connecting to card."); if (cmdline.soc_mode_counter && !soc_main(ctx, card, &cmdline)) goto err; if (cmdline.pxs_mode_counter && !paccess_main(ctx, card, &cmdline)) goto err; if (cmdline.soc_mode_counter == 0 && cmdline.pxs_mode_counter == 0 && (cmdline.verify_pin_given || cmdline.verify_bio_given || cmdline.verify_pin_or_bio_given) && !soc_main(ctx, card, &cmdline)) goto err; fail = 0; err: sc_disconnect_card(card); sc_release_context(ctx); cmdline_parser_free (&cmdline); return fail; } /*printf("%s:%d\n", __FILE__, __LINE__);*/