/* * card-starcos.c: Support for STARCOS SPK 2.3 cards * * Copyright (C) 2003 Jörn Zukowski and * Nils Larsch , TrustCenter AG * * 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 */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include "asn1.h" #include "cardctl.h" #include "internal.h" #include "iso7816.h" static const struct sc_atr_table starcos_atrs[] = { { "3B:B7:94:00:c0:24:31:fe:65:53:50:4b:32:33:90:00:b4", NULL, NULL, SC_CARD_TYPE_STARCOS_GENERIC, 0, NULL }, { "3B:B7:94:00:81:31:fe:65:53:50:4b:32:33:90:00:d1", NULL, NULL, SC_CARD_TYPE_STARCOS_GENERIC, 0, NULL }, { "3b:b7:18:00:c0:3e:31:fe:65:53:50:4b:32:34:90:00:25", NULL, NULL, SC_CARD_TYPE_STARCOS_GENERIC, 0, NULL }, { "3b:d8:18:ff:81:b1:fe:45:1f:03:80:64:04:1a:b4:03:81:05:61", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_4, 0, NULL }, { "3b:d3:96:ff:81:b1:fe:45:1f:07:80:81:05:2d", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_4, 0, NULL }, { "3B:9B:96:C0:0A:31:FE:45:80:67:04:1E:B5:01:00:89:4C:81:05:45", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5, 0, NULL }, { "3B:DB:96:FF:81:31:FE:45:80:67:05:34:B5:02:01:C0:A1:81:05:3C", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5, 0, NULL }, { "3B:D9:96:FF:81:31:FE:45:80:31:B8:73:86:01:C0:81:05:02", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5, 0, NULL }, { "3B:DF:96:FF:81:31:FE:45:80:5B:44:45:2E:42:4E:4F:54:4B:31:31:31:81:05:A0", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5, 0, NULL }, { "3B:DF:96:FF:81:31:FE:45:80:5B:44:45:2E:42:4E:4F:54:4B:31:30:30:81:05:A0", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5, 0, NULL }, { "3B:D9:96:FF:81:31:FE:45:80:31:B8:73:86:01:E0:81:05:22", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5, 0, NULL }, { "3B:D0:97:FF:81:B1:FE:45:1F:07:2B", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_4, 0, NULL }, { "3b:df:96:ff:81:31:fe:45:80:5b:44:45:2e:42:41:5f:53:43:33:35:32:81:05:b5", NULL, NULL, SC_CARD_TYPE_STARCOS_V3_5, 0, NULL }, { NULL, NULL, NULL, 0, 0, NULL } }; static struct sc_card_operations starcos_ops; static struct sc_card_operations *iso_ops = NULL; static struct sc_card_driver starcos_drv = { "STARCOS", "starcos", &starcos_ops, NULL, 0, NULL }; static const struct sc_card_error starcos_errors[] = { { 0x6600, SC_ERROR_INCORRECT_PARAMETERS, "Error setting the security env"}, { 0x66F0, SC_ERROR_INCORRECT_PARAMETERS, "No space left for padding"}, { 0x69F0, SC_ERROR_NOT_ALLOWED, "Command not allowed"}, { 0x6A89, SC_ERROR_FILE_ALREADY_EXISTS, "Files exists"}, { 0x6A8A, SC_ERROR_FILE_ALREADY_EXISTS, "Application exists"}, { 0x6F01, SC_ERROR_CARD_CMD_FAILED, "public key not complete"}, { 0x6F02, SC_ERROR_CARD_CMD_FAILED, "data overflow"}, { 0x6F03, SC_ERROR_CARD_CMD_FAILED, "invalid command sequence"}, { 0x6F05, SC_ERROR_CARD_CMD_FAILED, "security environment invalid"}, { 0x6F07, SC_ERROR_FILE_NOT_FOUND, "key part not found"}, { 0x6F08, SC_ERROR_CARD_CMD_FAILED, "signature failed"}, { 0x6F0A, SC_ERROR_INCORRECT_PARAMETERS, "key format does not match key length"}, { 0x6F0B, SC_ERROR_INCORRECT_PARAMETERS, "length of key component inconsistent with algorithm"}, { 0x6F81, SC_ERROR_CARD_CMD_FAILED, "system error"} }; /* internal structure to save the current security environment */ typedef struct starcos_ex_data_st { int sec_ops; /* the currently selected security operation, * i.e. SC_SEC_OPERATION_AUTHENTICATE etc. */ unsigned int fix_digestInfo; unsigned int pin_encoding; } starcos_ex_data; #define PIN_ENCODING_DETERMINE 0 #define PIN_ENCODING_DEFAULT SC_PIN_ENCODING_GLP // known pin formats for StarCOS 3.x cards #define PIN_FORMAT_F1 0x11 #define PIN_FORMAT_F2 0x12 #define PIN_FORMAT_RSA 0x1230 #define PIN_FORMAT_BCD 0x13 #define PIN_FORMAT_ASCII 0x14 #define PIN_FORMAT_PW_ASCII 0x21 // default is the Format 2 PIN Block which is GLP in OpenSC #define PIN_FORMAT_DEFAULT PIN_FORMAT_F2 #define CHECK_NOT_SUPPORTED_V3_4(card) \ do { \ if ((card)->type == SC_CARD_TYPE_STARCOS_V3_4) { \ sc_log((card)->ctx, \ "not supported for STARCOS 3.4 cards"); \ return SC_ERROR_NOT_SUPPORTED; \ } \ } while (0); /* the starcos part */ static int starcos_match_card(sc_card_t *card) { int i; i = _sc_match_atr(card, starcos_atrs, &card->type); if (i < 0) return 0; return 1; } typedef struct starcos_ctrl_ref_template_st { unsigned int transmission_format; #if 0 // not relevant values for now unsigned int se_reference; unsigned int ssec_initial_value; #endif } starcos_ctrl_ref_template; // tags #define TAG_STARCOS35_PIN_REFERENCE 0x88 #define TAG_STARCOS3X_SUPPORTED_SEC_MECHANISMS_tag 0x7B #define TAG_STARCOS3X_CTRL_REF_TEMPLATE 0xA4 #define TAG_STARCOS3X_TRANSMISSION_FORMAT 0x89 static const char * starcos_ef_pwdd = "3F000015"; static const char * starcos_ef_keyd = "3F000013"; /** * Parses supported securiy mechanisms record data. * It returns SC_SUCCESS and the ctrl_ref_template structure data on success */ static int starcos_parse_supported_sec_mechanisms(struct sc_card *card, const unsigned char * buf, size_t buflen, starcos_ctrl_ref_template * ctrl_ref_template) { struct sc_context *ctx = card->ctx; const unsigned char *supported_sec_mechanisms_tag = NULL; size_t taglen; LOG_FUNC_CALLED(ctx); supported_sec_mechanisms_tag = sc_asn1_find_tag(ctx, buf, buflen, TAG_STARCOS3X_SUPPORTED_SEC_MECHANISMS_tag, &taglen); if (supported_sec_mechanisms_tag != NULL && taglen >= 1) { const unsigned char *tx_fmt_tag = NULL; const unsigned char *ctrl_ref_template_tag = NULL; size_t supported_sec_mechanisms_taglen = taglen; // control-reference template is either included in the supported security mechanisms tag or it can be the CRT tag itself (EF.PWDD) ctrl_ref_template_tag = sc_asn1_find_tag(ctx, supported_sec_mechanisms_tag, taglen, TAG_STARCOS3X_CTRL_REF_TEMPLATE, &taglen); if ( ctrl_ref_template_tag == NULL || taglen == 0 ) { ctrl_ref_template_tag = supported_sec_mechanisms_tag; taglen = supported_sec_mechanisms_taglen; } tx_fmt_tag = sc_asn1_find_tag(ctx, ctrl_ref_template_tag, taglen, TAG_STARCOS3X_TRANSMISSION_FORMAT, &taglen); if ( tx_fmt_tag != NULL && taglen >= 1 ) { ctrl_ref_template->transmission_format = *(tx_fmt_tag + 0); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } } LOG_FUNC_RETURN(ctx, SC_ERROR_TEMPLATE_NOT_FOUND); } static int starcos_determine_pin_format34(sc_card_t *card, unsigned int * pin_format) { struct sc_context *ctx = card->ctx; struct sc_path path; struct sc_file *file; unsigned char buf[256]; int rv; int retval = SC_SUCCESS; int rec_no=1; LOG_FUNC_CALLED(ctx); sc_format_path(starcos_ef_pwdd, &path); rv = sc_select_file(card, &path, &file); LOG_TEST_RET(ctx, rv, "Cannot select EF.PWDD file"); if ( (rv = sc_read_record(card, rec_no, buf, sizeof(buf), SC_RECORD_BY_REC_NR)) > 0 ) { starcos_ctrl_ref_template ctrl_ref_template; memset((void*)&ctrl_ref_template, 0, sizeof(ctrl_ref_template)); rv = starcos_parse_supported_sec_mechanisms(card, buf, rv, &ctrl_ref_template); if ( rv == SC_SUCCESS ) { *pin_format = ctrl_ref_template.transmission_format; sc_log(ctx, "Determined StarCOS 3.4 PIN format: 0x%x", *pin_format); } else { sc_log(ctx, "Failed to parse record %d of EF.PWD, err=%d", rec_no, rv); retval = rv; } } else { sc_log(ctx, "Failed to read record %d of EF.PWDD, err=%d", rec_no, rv); retval = rv; } sc_file_free(file); LOG_FUNC_RETURN(ctx, retval); } static int starcos_determine_pin_format35(sc_card_t *card, unsigned int * pin_format) { struct sc_context *ctx = card->ctx; struct sc_path path; struct sc_file *file; unsigned char buf[256]; int rv; int retval = SC_ERROR_RECORD_NOT_FOUND; int rec_no=1; starcos_ctrl_ref_template ctrl_ref_template; LOG_FUNC_CALLED(ctx); sc_format_path(starcos_ef_keyd, &path); rv = sc_select_file(card, &path, &file); LOG_TEST_RET(ctx, rv, "Cannot select EF.KEYD file"); while ( (rv = sc_read_record(card, rec_no++, buf, sizeof(buf), SC_RECORD_BY_REC_NR)) > 0 ) { if ( buf[0] != TAG_STARCOS35_PIN_REFERENCE ) continue; memset((void*)&ctrl_ref_template, 0, sizeof(ctrl_ref_template)); rv = starcos_parse_supported_sec_mechanisms(card, buf, rv, &ctrl_ref_template); if ( rv == SC_SUCCESS ) { *pin_format = ctrl_ref_template.transmission_format; sc_log(ctx, "Determined StarCOS 3.5 PIN format: 0x%x", *pin_format); retval = rv; // assuming that all PINs and PUKs have the same transmission format break; } else { sc_log(ctx, "Failed to parse record %d of EF.KEYD, err=%d", rec_no-1, rv); retval = rv; } } sc_file_free(file); LOG_FUNC_RETURN(ctx, retval); } /** * Determine v3.x PIN encoding by parsing either * EF.PWDD (for v3.4) or EF.KEYD (for v3.5) * * It returns an OpenSC PIN encoding, using the default value on failure */ static unsigned int starcos_determine_pin_encoding(sc_card_t *card) { unsigned int pin_format = PIN_FORMAT_DEFAULT; unsigned int encoding = PIN_ENCODING_DETERMINE; if ( card->type == SC_CARD_TYPE_STARCOS_V3_4 ) { starcos_determine_pin_format34(card, &pin_format); } else if ( card->type == SC_CARD_TYPE_STARCOS_V3_5 ) { starcos_determine_pin_format35(card, &pin_format); } switch (pin_format) { case PIN_FORMAT_PW_ASCII: case PIN_FORMAT_ASCII: encoding = SC_PIN_ENCODING_ASCII; break; case PIN_FORMAT_BCD: encoding = SC_PIN_ENCODING_BCD; break; case PIN_FORMAT_F1: case PIN_FORMAT_F2: encoding = SC_PIN_ENCODING_GLP; break; } sc_log(card->ctx, "Determined PIN encoding: %d", encoding); return encoding; } static int starcos_init(sc_card_t *card) { unsigned int flags; starcos_ex_data *ex_data; ex_data = calloc(1, sizeof(starcos_ex_data)); if (ex_data == NULL) return SC_ERROR_OUT_OF_MEMORY; card->name = "STARCOS"; card->cla = 0x00; card->drv_data = (void *)ex_data; ex_data->pin_encoding = PIN_ENCODING_DETERMINE; flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_ONBOARD_KEY_GEN | SC_ALGORITHM_RSA_PAD_ISO9796 | SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_RSA_HASH_SHA1 | SC_ALGORITHM_RSA_HASH_MD5 | SC_ALGORITHM_RSA_HASH_RIPEMD160 | SC_ALGORITHM_RSA_HASH_MD5_SHA1; card->caps = SC_CARD_CAP_RNG; if (card->type == SC_CARD_TYPE_STARCOS_V3_4 || card->type == SC_CARD_TYPE_STARCOS_V3_5) { if (card->type == SC_CARD_TYPE_STARCOS_V3_4) card->name = "STARCOS 3.4"; else card->name = "STARCOS 3.5"; card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; card->caps |= SC_CARD_CAP_APDU_EXT; flags |= SC_CARD_FLAG_RNG | SC_ALGORITHM_RSA_HASH_SHA224 | SC_ALGORITHM_RSA_HASH_SHA256 | SC_ALGORITHM_RSA_HASH_SHA384 | SC_ALGORITHM_RSA_HASH_SHA512; _sc_card_add_rsa_alg(card, 512, flags, 0x10001); _sc_card_add_rsa_alg(card, 768, flags, 0x10001); _sc_card_add_rsa_alg(card,1024, flags, 0x10001); _sc_card_add_rsa_alg(card,1728, flags, 0x10001); _sc_card_add_rsa_alg(card,1976, flags, 0x10001); _sc_card_add_rsa_alg(card,2048, flags, 0x10001); } else { _sc_card_add_rsa_alg(card, 512, flags, 0x10001); _sc_card_add_rsa_alg(card, 768, flags, 0x10001); _sc_card_add_rsa_alg(card,1024, flags, 0x10001); /* we need read_binary&friends with max 128 bytes per read */ card->max_send_size = 128; card->max_recv_size = 128; } if (sc_parse_ef_atr(card) == SC_SUCCESS) { if (card->ef_atr->card_capabilities & ISO7816_CAP_EXTENDED_LENGTH) { card->caps |= SC_CARD_CAP_APDU_EXT; } if (card->ef_atr->max_response_apdu > 0) { card->max_recv_size = card->ef_atr->max_response_apdu; } if (card->ef_atr->max_command_apdu > 0) { card->max_send_size = card->ef_atr->max_command_apdu; } } if ( ex_data->pin_encoding == PIN_ENCODING_DETERMINE ) { // about to determine PIN encoding ex_data->pin_encoding = starcos_determine_pin_encoding(card); } return 0; } static int starcos_finish(sc_card_t *card) { if (card->drv_data) free((starcos_ex_data *)card->drv_data); return 0; } static int process_fci(sc_context_t *ctx, sc_file_t *file, const u8 *buf, size_t buflen) { /* NOTE: According to the Starcos S 2.1 manual it's possible * that a SELECT DF returns as a FCI arbitrary data which * is stored in a object file (in the corresponding DF) * with the tag 0x6f. */ size_t taglen, len = buflen; const u8 *tag = NULL, *p; sc_log(ctx, "processing FCI bytes\n"); if (buflen < 2) return SC_ERROR_INTERNAL; if (buf[0] != 0x6f) return SC_ERROR_INVALID_DATA; len = (size_t)buf[1]; if (buflen - 2 < len) return SC_ERROR_INVALID_DATA; p = buf + 2; /* defaults */ file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_UNKNOWN; file->shareable = 0; file->record_length = 0; file->size = 0; tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; sc_log(ctx, " bytes in file: %d\n", bytes); file->size = bytes; } tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); if (tag != NULL) { const char *type = "unknown"; const char *structure = "unknown"; if (taglen == 1 && tag[0] == 0x01) { /* transparent EF */ type = "working EF"; structure = "transparent"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; } else if (taglen == 1 && tag[0] == 0x11) { /* object EF */ type = "working EF"; structure = "object"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; /* TODO */ } else if (taglen == 3 && tag[1] == 0x21) { type = "working EF"; file->record_length = tag[2]; file->type = SC_FILE_TYPE_WORKING_EF; /* linear fixed, cyclic or compute */ switch ( tag[0] ) { case 0x02: structure = "linear fixed"; file->ef_structure = SC_FILE_EF_LINEAR_FIXED; break; case 0x07: structure = "cyclic"; file->ef_structure = SC_FILE_EF_CYCLIC; break; case 0x17: structure = "compute"; file->ef_structure = SC_FILE_EF_UNKNOWN; break; default: structure = "unknown"; file->ef_structure = SC_FILE_EF_UNKNOWN; file->record_length = 0; break; } } sc_log(ctx, " type: %s\n", type); sc_log(ctx, " EF structure: %s\n", structure); } file->magic = SC_FILE_MAGIC; return SC_SUCCESS; } static int process_fci_v3_4(sc_context_t *ctx, sc_file_t *file, const u8 *buf, size_t buflen) { size_t taglen, len = buflen; const u8 *tag = NULL, *p; sc_log(ctx, "processing %"SC_FORMAT_LEN_SIZE_T"u FCI bytes\n", buflen); if (buflen < 2) return SC_ERROR_INTERNAL; if (buf[0] != 0x6f) return SC_ERROR_INVALID_DATA; len = (size_t)buf[1]; if (buflen - 2 < len) return SC_ERROR_INVALID_DATA; /* defaults */ file->type = SC_FILE_TYPE_WORKING_EF; if (len == 0) { SC_FUNC_RETURN(ctx, 2, SC_SUCCESS); } p = buf + 2; file->ef_structure = SC_FILE_TYPE_DF; file->shareable = 1; tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen); if (tag != NULL && taglen > 0 && taglen <= 16) { memcpy(file->name, tag, taglen); file->namelen = taglen; sc_log(ctx, "filename %s", sc_dump_hex(file->name, file->namelen)); } return SC_SUCCESS; } static int process_fcp_v3_4(sc_context_t *ctx, sc_file_t *file, const u8 *buf, size_t buflen) { size_t taglen, len = buflen; const u8 *tag = NULL, *p; sc_log(ctx, "processing %"SC_FORMAT_LEN_SIZE_T"u FCP bytes\n", buflen); if (buflen < 2) return SC_ERROR_INTERNAL; if (buf[0] != 0x62) return SC_ERROR_INVALID_DATA; len = (size_t)buf[1]; if (buflen - 2 < len) return SC_ERROR_INVALID_DATA; p = buf + 2; tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; sc_log(ctx, " bytes in file: %d\n", bytes); file->size = bytes; } tag = sc_asn1_find_tag(ctx, p, len, 0xc5, &taglen); if (tag != NULL && taglen >= 2) { int bytes = (tag[0] << 8) + tag[1]; sc_log(ctx, " bytes in file 2: %d\n", bytes); file->size = bytes; } tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); if (tag != NULL) { const char *type = "unknown"; const char *structure = "unknown"; if (taglen >= 1) { unsigned char byte = tag[0]; if (byte & 0x40) { file->shareable = 1; } if (byte == 0x38) { type = "DF"; file->type = SC_FILE_TYPE_DF; file->shareable = 1; } switch (byte & 7) { case 1: /* transparent EF */ type = "working EF"; structure = "transparent"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; break; case 2: /* linear fixed EF */ type = "working EF"; structure = "linear fixed"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_LINEAR_FIXED; break; case 4: /* linear variable EF */ type = "working EF"; structure = "linear variable"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_LINEAR_VARIABLE; break; case 6: /* cyclic EF */ type = "working EF"; structure = "cyclic"; file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_CYCLIC; break; default: /* use defaults from above */ break; } } sc_log(ctx, " type: %s\n", type); sc_log(ctx, " EF structure: %s\n", structure); if (taglen >= 2) { if (tag[1] != 0x41 || taglen != 5) { SC_FUNC_RETURN(ctx, 2,SC_ERROR_INVALID_DATA); } /* formatted EF */ file->record_length = (tag[2] << 8) + tag[3]; file->record_count = tag[4]; sc_log(ctx, " rec_len: %"SC_FORMAT_LEN_SIZE_T"u rec_cnt: %"SC_FORMAT_LEN_SIZE_T"u\n\n", file->record_length, file->record_count); } } tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen); if (tag != NULL && taglen >= 2) { file->id = (tag[0] << 8) | tag[1]; sc_log(ctx, " file identifier: 0x%02X%02X\n", tag[0], tag[1]); } tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen); if (tag != NULL && taglen > 0 && taglen <= 16) { memcpy(file->name, tag, taglen); file->namelen = taglen; sc_log(ctx, " filename %s", sc_dump_hex(file->name, file->namelen)); } tag = sc_asn1_find_tag(ctx, p, len, 0x8a, &taglen); if (tag != NULL && taglen == 1) { char* status = "unknown"; switch (tag[0]) { case 1: status = "creation"; file->status = SC_FILE_STATUS_CREATION; break; case 5: status = "operational active"; file->status = SC_FILE_STATUS_ACTIVATED; break; case 12: case 13: status = "creation"; file->status = SC_FILE_STATUS_INVALIDATED; break; default: break; } sc_log(ctx, " file status: %s\n", status); } file->magic = SC_FILE_MAGIC; return SC_SUCCESS; } static int starcos_select_aid(sc_card_t *card, u8 aid[16], size_t len, sc_file_t **file_out) { sc_apdu_t apdu; int r; size_t i = 0; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x0C); apdu.lc = len; apdu.data = (u8*)aid; apdu.datalen = len; apdu.resplen = 0; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); /* check return value */ if (!(apdu.sw1 == 0x90 && apdu.sw2 == 0x00) && apdu.sw1 != 0x61 ) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); /* update cache */ card->cache.current_path.type = SC_PATH_TYPE_DF_NAME; card->cache.current_path.len = len; memcpy(card->cache.current_path.value, aid, len); if (file_out) { sc_file_t *file = sc_file_new(); if (!file) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->type = SC_FILE_TYPE_DF; file->ef_structure = SC_FILE_EF_UNKNOWN; file->path.len = 0; file->size = 0; /* AID */ for (i = 0; i < len; i++) file->name[i] = aid[i]; file->namelen = len; file->id = 0x0000; file->magic = SC_FILE_MAGIC; *file_out = file; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int starcos_select_fid(sc_card_t *card, unsigned int id_hi, unsigned int id_lo, sc_file_t **file_out, int is_file) { sc_apdu_t apdu; u8 data[] = {id_hi & 0xff, id_lo & 0xff}; u8 resp[SC_MAX_APDU_BUFFER_SIZE]; int bIsDF = 0, r; int isFCP = 0; int isMF = 0; /* request FCI to distinguish between EFs and DFs */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00); apdu.p2 = 0x00; apdu.resp = (u8*)resp; apdu.resplen = SC_MAX_APDU_BUFFER_SIZE; apdu.le = 256; apdu.lc = 2; apdu.data = (u8*)data; apdu.datalen = 2; if (card->type == SC_CARD_TYPE_STARCOS_V3_4 || card->type == SC_CARD_TYPE_STARCOS_V3_5) { if (id_hi == 0x3f && id_lo == 0x0) { apdu.p1 = 0x0; apdu.p2 = 0x0; isMF = 1; } else if (file_out || is_file) { // last component (i.e. file or path) apdu.p1 = 0x2; apdu.p2 = 0x4; } else { // path component apdu.p1 = 0x1; apdu.p2 = 0x0; } } r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.p2 == 0x00 && apdu.sw1 == 0x62 && apdu.sw2 == 0x84 ) { /* no FCI => we have a DF (see comment in process_fci()) */ bIsDF = 1; apdu.p2 = 0x0C; apdu.cse = SC_APDU_CASE_3_SHORT; apdu.resplen = 0; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU re-transmit failed"); } else if ((card->type == SC_CARD_TYPE_STARCOS_V3_4 || card->type == SC_CARD_TYPE_STARCOS_V3_5) && apdu.p2 == 0x4 && apdu.sw1 == 0x6a && apdu.sw2 == 0x82) { /* not a file, could be a path */ bIsDF = 1; apdu.p1 = 0x1; apdu.p2 = 0x0; apdu.resplen = sizeof(resp); apdu.le = 256; apdu.lc = 2; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU re-transmit failed"); } else if (apdu.sw1 == 0x61 || (apdu.sw1 == 0x90 && apdu.sw2 == 0x00 && !isMF)) { /* SELECT returned some data (possible FCI) => * try a READ BINARY to see if a EF is selected */ sc_apdu_t apdu2; u8 resp2[2]; sc_format_apdu(card, &apdu2, SC_APDU_CASE_2_SHORT, 0xB0, 0, 0); apdu2.resp = (u8*)resp2; apdu2.resplen = 2; apdu2.le = 1; apdu2.lc = 0; r = sc_transmit_apdu(card, &apdu2); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu2.sw1 == 0x69 && apdu2.sw2 == 0x86) { /* no current EF is selected => we have a DF */ bIsDF = 1; } else { isFCP = 1; } } if (apdu.sw1 != 0x61 && (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); /* update cache */ if (bIsDF || isMF) { card->cache.current_path.type = SC_PATH_TYPE_PATH; card->cache.current_path.value[0] = 0x3f; card->cache.current_path.value[1] = 0x00; if (id_hi == 0x3f && id_lo == 0x00) card->cache.current_path.len = 2; else { card->cache.current_path.len = 4; card->cache.current_path.value[2] = id_hi; card->cache.current_path.value[3] = id_lo; } } if (file_out) { sc_file_t *file = sc_file_new(); if (!file) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->id = (id_hi << 8) + id_lo; file->path = card->cache.current_path; if (bIsDF) { /* we have a DF */ file->type = SC_FILE_TYPE_DF; file->ef_structure = SC_FILE_EF_UNKNOWN; file->size = 0; file->namelen = 0; file->magic = SC_FILE_MAGIC; *file_out = file; } else { /* ok, assume we have a EF */ if (card->type == SC_CARD_TYPE_STARCOS_V3_4 || card->type == SC_CARD_TYPE_STARCOS_V3_5) { if (isFCP) { r = process_fcp_v3_4(card->ctx, file, apdu.resp, apdu.resplen); } else { r = process_fci_v3_4(card->ctx, file, apdu.resp, apdu.resplen); } } else { r = process_fci(card->ctx, file, apdu.resp, apdu.resplen); } if (r != SC_SUCCESS) { sc_file_free(file); return r; } *file_out = file; } } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } static int starcos_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; int r; size_t i, pathlen; char pbuf[SC_MAX_PATH_STRING_SIZE]; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = sc_path_print(pbuf, sizeof(pbuf), &card->cache.current_path); if (r != SC_SUCCESS) pbuf[0] = '\0'; sc_log(card->ctx, "current path (%s, %s): %s (len: %"SC_FORMAT_LEN_SIZE_T"u)\n", card->cache.current_path.type == SC_PATH_TYPE_DF_NAME ? "aid" : "path", card->cache.valid ? "valid" : "invalid", pbuf, card->cache.current_path.len); memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; if (in_path->type == SC_PATH_TYPE_FILE_ID) { /* SELECT EF/DF with ID */ /* Select with 2byte File-ID */ if (pathlen != 2) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,SC_ERROR_INVALID_ARGUMENTS); return starcos_select_fid(card, path[0], path[1], file_out, 1); } else if (in_path->type == SC_PATH_TYPE_DF_NAME) { /* SELECT DF with AID */ /* Select with 1-16byte Application-ID */ if (card->cache.valid && card->cache.current_path.type == SC_PATH_TYPE_DF_NAME && card->cache.current_path.len == pathlen && memcmp(card->cache.current_path.value, pathbuf, pathlen) == 0 ) { sc_log(card->ctx, "cache hit\n"); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); } else return starcos_select_aid(card, pathbuf, pathlen, file_out); } else if (in_path->type == SC_PATH_TYPE_PATH) { u8 n_pathbuf[SC_MAX_PATH_SIZE]; int bMatch = -1; /* Select with path (sequence of File-IDs) */ /* Starcos (S 2.1 and SPK 2.3) only supports one * level of subdirectories, therefore a path is * at most 3 FID long (the last one being the FID * of a EF) => pathlen must be even and less than 6 */ if (pathlen%2 != 0 || pathlen > 6 || pathlen <= 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); /* if pathlen == 6 then the first FID must be MF (== 3F00) */ if (pathlen == 6 && ( path[0] != 0x3f || path[1] != 0x00 )) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); if (card->type != SC_CARD_TYPE_STARCOS_V3_4 && card->type != SC_CARD_TYPE_STARCOS_V3_5) { /* unify path (the first FID should be MF) */ if (path[0] != 0x3f || path[1] != 0x00) { n_pathbuf[0] = 0x3f; n_pathbuf[1] = 0x00; memcpy(n_pathbuf+2, path, pathlen); path = n_pathbuf; pathlen += 2; } } /* check current working directory */ if (card->cache.valid && card->cache.current_path.type == SC_PATH_TYPE_PATH && card->cache.current_path.len >= 2 && card->cache.current_path.len <= pathlen ) { bMatch = 0; for (i=0; i < card->cache.current_path.len; i+=2) if (card->cache.current_path.value[i] == path[i] && card->cache.current_path.value[i+1] == path[i+1] ) bMatch += 2; if ((card->type == SC_CARD_TYPE_STARCOS_V3_4 || card->type == SC_CARD_TYPE_STARCOS_V3_5) && bMatch > 0 && (size_t) bMatch < card->cache.current_path.len) { /* we're in the wrong folder, start traversing from root */ bMatch = 0; card->cache.current_path.len = 0; } } if ( card->cache.valid && bMatch >= 0 ) { if ( pathlen - bMatch == 2 ) /* we are in the right directory */ return starcos_select_fid(card, path[bMatch], path[bMatch+1], file_out, 1); else if ( pathlen - bMatch > 2 ) { /* two more steps to go */ sc_path_t new_path; /* first step: change directory */ r = starcos_select_fid(card, path[bMatch], path[bMatch+1], NULL, 0); LOG_TEST_RET(card->ctx, r, "SELECT FILE (DF-ID) failed"); memset(&new_path, 0, sizeof(sc_path_t)); new_path.type = SC_PATH_TYPE_PATH; new_path.len = pathlen - bMatch-2; memcpy(new_path.value, &(path[bMatch+2]), new_path.len); /* final step: select file */ return starcos_select_file(card, &new_path, file_out); } else /* if (bMatch - pathlen == 0) */ { /* done: we are already in the * requested directory */ sc_log(card->ctx, "cache hit\n"); /* copy file info (if necessary) */ if (file_out) { sc_file_t *file = sc_file_new(); if (!file) LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); file->id = (path[pathlen-2] << 8) + path[pathlen-1]; file->path = card->cache.current_path; file->type = SC_FILE_TYPE_DF; file->ef_structure = SC_FILE_EF_UNKNOWN; file->size = 0; file->namelen = 0; file->magic = SC_FILE_MAGIC; *file_out = file; } /* nothing left to do */ return SC_SUCCESS; } } else { /* no usable cache */ for ( i=0; ictx, r, "SELECT FILE (DF-ID) failed"); } return starcos_select_fid(card, path[pathlen-2], path[pathlen-1], file_out, 1); } } else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } static int starcos_get_challenge(struct sc_card *card, unsigned char *rnd, size_t len) { LOG_FUNC_CALLED(card->ctx); if (len > 8) { len = 8; } LOG_FUNC_RETURN(card->ctx, iso_ops->get_challenge(card, rnd, len)); } #define STARCOS_AC_ALWAYS 0x9f #define STARCOS_AC_NEVER 0x5f #define STARCOS_PINID2STATE(a) ((((a) & 0x0f) == 0x01) ? ((a) & 0x0f) : (0x0f - ((0x0f & (a)) >> 1))) static u8 process_acl_entry(sc_file_t *in, unsigned int method, unsigned int in_def) { u8 def = (u8)in_def; const sc_acl_entry_t *entry = sc_file_get_acl_entry(in, method); if (!entry) return def; else if (entry->method & SC_AC_CHV) { unsigned int key_ref = entry->key_ref; if (key_ref == SC_AC_KEY_REF_NONE) return def; else if ((key_ref & 0x0f) == 1) /* SOPIN */ return (key_ref & 0x80 ? 0x10 : 0x00) | 0x01; else return (key_ref & 0x80 ? 0x10 : 0x00) | STARCOS_PINID2STATE(key_ref); } else if (entry->method & SC_AC_NEVER) return STARCOS_AC_NEVER; else return def; } /** starcos_process_acl * \param card pointer to the sc_card object * \param file pointer to the sc_file object * \param data pointer to a sc_starcos_create_data structure * \return SC_SUCCESS if no error occurred otherwise error code * * This function tries to create a somewhat usable Starcos spk 2.3 acl * from the OpenSC internal acl (storing the result in the supplied * sc_starcos_create_data structure). */ static int starcos_process_acl(sc_card_t *card, sc_file_t *file, sc_starcos_create_data *data) { u8 tmp, *p; static const u8 def_key[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}; if (file->type == SC_FILE_TYPE_DF && file->id == 0x3f00) { p = data->data.mf.header; memcpy(p, def_key, 8); p += 8; *p++ = (file->size >> 8) & 0xff; *p++ = file->size & 0xff; /* guess isf size (mf_size / 4) */ *p++ = (file->size >> 10) & 0xff; *p++ = (file->size >> 2) & 0xff; /* ac create ef */ *p++ = process_acl_entry(file,SC_AC_OP_CREATE,STARCOS_AC_ALWAYS); /* ac create key */ *p++ = process_acl_entry(file,SC_AC_OP_CREATE,STARCOS_AC_ALWAYS); /* ac create df */ *p++ = process_acl_entry(file,SC_AC_OP_CREATE,STARCOS_AC_ALWAYS); /* use the same ac for register df and create df */ *p++ = data->data.mf.header[14]; /* if sm is required use combined mode */ if (file->acl[SC_AC_OP_CREATE] && (sc_file_get_acl_entry(file, SC_AC_OP_CREATE))->method & SC_AC_PRO) tmp = 0x03; /* combinde mode */ else tmp = 0x00; /* no sm */ *p++ = tmp; /* use the same sm mode for all ops */ *p++ = tmp; *p = tmp; data->type = SC_STARCOS_MF_DATA; return SC_SUCCESS; } else if (file->type == SC_FILE_TYPE_DF){ p = data->data.df.header; *p++ = (file->id >> 8) & 0xff; *p++ = file->id & 0xff; if (file->namelen) { /* copy aid */ *p++ = file->namelen & 0xff; memset(p, 0, 16); memcpy(p, file->name, (u8)file->namelen); p += 16; } else { /* (mis)use the fid as aid */ *p++ = 2; memset(p, 0, 16); *p++ = (file->id >> 8) & 0xff; *p++ = file->id & 0xff; p += 14; } /* guess isf size */ *p++ = (file->size >> 10) & 0xff; /* ISF space */ *p++ = (file->size >> 2) & 0xff; /* ISF space */ /* ac create ef */ *p++ = process_acl_entry(file,SC_AC_OP_CREATE,STARCOS_AC_ALWAYS); /* ac create key */ *p++ = process_acl_entry(file,SC_AC_OP_CREATE,STARCOS_AC_ALWAYS); /* set sm byte (same for keys and ef) */ if (file->acl[SC_AC_OP_CREATE] && (sc_file_get_acl_entry(file, SC_AC_OP_CREATE)->method & SC_AC_PRO)) tmp = 0x03; else tmp = 0x00; *p++ = tmp; /* SM CR */ *p = tmp; /* SM ISF */ data->data.df.size[0] = (file->size >> 8) & 0xff; data->data.df.size[1] = file->size & 0xff; data->type = SC_STARCOS_DF_DATA; return SC_SUCCESS; } else if (file->type == SC_FILE_TYPE_WORKING_EF) { p = data->data.ef.header; *p++ = (file->id >> 8) & 0xff; *p++ = file->id & 0xff; /* ac read */ *p++ = process_acl_entry(file, SC_AC_OP_READ,STARCOS_AC_ALWAYS); /* ac write */ *p++ = process_acl_entry(file, SC_AC_OP_WRITE,STARCOS_AC_ALWAYS); /* ac erase */ *p++ = process_acl_entry(file, SC_AC_OP_ERASE,STARCOS_AC_ALWAYS); *p++ = STARCOS_AC_ALWAYS; /* AC LOCK */ *p++ = STARCOS_AC_ALWAYS; /* AC UNLOCK */ *p++ = STARCOS_AC_ALWAYS; /* AC INCREASE */ *p++ = STARCOS_AC_ALWAYS; /* AC DECREASE */ *p++ = 0x00; /* rfu */ *p++ = 0x00; /* rfu */ /* use sm (in combined mode) if wanted */ if ((file->acl[SC_AC_OP_READ] && (sc_file_get_acl_entry(file, SC_AC_OP_READ)->method & SC_AC_PRO)) || (file->acl[SC_AC_OP_UPDATE] && (sc_file_get_acl_entry(file, SC_AC_OP_UPDATE)->method & SC_AC_PRO)) || (file->acl[SC_AC_OP_WRITE] && (sc_file_get_acl_entry(file, SC_AC_OP_WRITE)->method & SC_AC_PRO)) ) tmp = 0x03; else tmp = 0x00; *p++ = tmp; /* SM byte */ *p++ = 0x00; /* use the least significant 5 bits * of the FID as SID */ switch (file->ef_structure) { case SC_FILE_EF_TRANSPARENT: *p++ = 0x81; *p++ = (file->size >> 8) & 0xff; *p = file->size & 0xff; break; case SC_FILE_EF_LINEAR_FIXED: *p++ = 0x82; *p++ = file->record_count & 0xff; *p = file->record_length & 0xff; break; case SC_FILE_EF_CYCLIC: *p++ = 0x84; *p++ = file->record_count & 0xff; *p = file->record_length & 0xff; break; default: return SC_ERROR_INVALID_ARGUMENTS; } data->type = SC_STARCOS_EF_DATA; return SC_SUCCESS; } else return SC_ERROR_INVALID_ARGUMENTS; } /** starcos_create_mf * internal function to create the MF * \param card pointer to the sc_card structure * \param data pointer to a sc_starcos_create_data object * \return SC_SUCCESS or error code * * This function creates the MF based on the information stored * in the sc_starcos_create_data.mf structure. Note: CREATE END must be * called separately to activate the ACs. */ static int starcos_create_mf(sc_card_t *card, sc_starcos_create_data *data) { int r; sc_apdu_t apdu; sc_context_t *ctx = card->ctx; CHECK_NOT_SUPPORTED_V3_4(card); sc_log(ctx, "creating MF \n"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); apdu.cla |= 0x80; apdu.lc = 19; apdu.datalen = 19; apdu.data = (u8 *) data->data.mf.header; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } /** starcos_create_df * internal function to create a DF * \param card pointer to the sc_card structure * \param data pointer to a sc_starcos_create_data object * \return SC_SUCCESS or error code * * This functions registers and creates a DF based in the information * stored in a sc_starcos_create_data.df data structure. Note: CREATE END must * be called separately to activate the ACs. */ static int starcos_create_df(sc_card_t *card, sc_starcos_create_data *data) { int r; size_t len; sc_apdu_t apdu; sc_context_t *ctx = card->ctx; CHECK_NOT_SUPPORTED_V3_4(card); sc_log(ctx, "creating DF\n"); /* first step: REGISTER DF */ sc_log(ctx, "calling REGISTER DF\n"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x52, data->data.df.size[0], data->data.df.size[1]); len = 3 + data->data.df.header[2]; apdu.cla |= 0x80; apdu.lc = len; apdu.datalen = len; apdu.data = data->data.df.header; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); /* second step: CREATE DF */ sc_log(ctx, "calling CREATE DF\n"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x01, 0x00); apdu.cla |= 0x80; apdu.lc = 25; apdu.datalen = 25; apdu.data = data->data.df.header; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } /** starcos_create_ef * internal function to create a EF * \param card pointer to the sc_card structure * \param data pointer to a sc_starcos_create_data object * \return SC_SUCCESS or error code * * This function creates a EF based on the information stored in * the sc_starcos_create_data.ef data structure. */ static int starcos_create_ef(sc_card_t *card, sc_starcos_create_data *data) { int r; sc_apdu_t apdu; sc_context_t *ctx = card->ctx; CHECK_NOT_SUPPORTED_V3_4(card); sc_log(ctx, "creating EF\n"); sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT,0xE0,0x03,0x00); apdu.cla |= 0x80; apdu.lc = 16; apdu.datalen = 16; apdu.data = (u8 *) data->data.ef.header; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } /** starcos_create_end * internal function to activate the ACs * \param card pointer to the sc_card structure * \param file pointer to a sc_file object * \return SC_SUCCESS or error code * * This function finishes the creation of a DF (or MF) and activates * the ACs. */ static int starcos_create_end(sc_card_t *card, sc_file_t *file) { int r; u8 fid[2]; sc_apdu_t apdu; if (file->type != SC_FILE_TYPE_DF) return SC_ERROR_INVALID_ARGUMENTS; CHECK_NOT_SUPPORTED_V3_4(card); fid[0] = (file->id >> 8) & 0xff; fid[1] = file->id & 0xff; sc_format_apdu(card,&apdu,SC_APDU_CASE_3_SHORT, 0xE0, 0x02, 0x00); apdu.cla |= 0x80; apdu.lc = 2; apdu.datalen = 2; apdu.data = fid; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); return sc_check_sw(card, apdu.sw1, apdu.sw2); } /** starcos_create_file * \param card pointer to the sc_card structure * \param file pointer to a sc_file object * \return SC_SUCCESS or error code * * This function creates MF, DF or EF based on the supplied * information in the sc_file structure (using starcos_process_acl). */ static int starcos_create_file(sc_card_t *card, sc_file_t *file) { int r; sc_starcos_create_data data; CHECK_NOT_SUPPORTED_V3_4(card); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (file->type == SC_FILE_TYPE_DF) { if (file->id == 0x3f00) { /* CREATE MF */ r = starcos_process_acl(card, file, &data); if (r != SC_SUCCESS) return r; return starcos_create_mf(card, &data); } else { /* CREATE DF */ r = starcos_process_acl(card, file, &data); if (r != SC_SUCCESS) return r; return starcos_create_df(card, &data); } } else if (file->type == SC_FILE_TYPE_WORKING_EF) { /* CREATE EF */ r = starcos_process_acl(card, file, &data); if (r != SC_SUCCESS) return r; return starcos_create_ef(card, &data); } else return SC_ERROR_INVALID_ARGUMENTS; } /** starcos_erase_card * internal function to restore the delivery state * \param card pointer to the sc_card object * \return SC_SUCCESS or error code * * This function deletes the MF (for 'test cards' only). */ static int starcos_erase_card(sc_card_t *card) { /* restore the delivery state */ int r; u8 sbuf[2]; sc_apdu_t apdu; sbuf[0] = 0x3f; sbuf[1] = 0x00; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); apdu.cla |= 0x80; apdu.lc = 2; apdu.datalen = 2; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); sc_invalidate_cache(card); if (apdu.sw1 == 0x69 && apdu.sw2 == 0x85) /* no MF to delete, ignore error */ return SC_SUCCESS; else return sc_check_sw(card, apdu.sw1, apdu.sw2); } #define STARCOS_WKEY_CSIZE 124 /** starcos_write_key * set key in isf * \param card pointer to the sc_card object * \param data pointer to a sc_starcos_wkey_data structure * \return SC_SUCCESS or error code * * This function installs a key header in the ISF (based on the * information supplied in the sc_starcos_wkey_data structure) * and set a supplied key (depending on the mode). */ static int starcos_write_key(sc_card_t *card, sc_starcos_wkey_data *data) { int r; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; const u8 *p; size_t len = sizeof(sbuf), tlen, offset = 0; sc_apdu_t apdu; CHECK_NOT_SUPPORTED_V3_4(card); if (data->mode == 0) { /* mode == 0 => install */ /* install key header */ sbuf[0] = 0xc1; /* key header tag */ sbuf[1] = 0x0c; /* key header length */ memcpy(sbuf + 2, data->key_header, 12); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xf4, data->mode, 0x00); apdu.cla |= 0x80; apdu.lc = 14; apdu.datalen = 14; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); if (data->key == NULL) return SC_SUCCESS; } if (data->key == NULL) return SC_ERROR_INVALID_ARGUMENTS; p = data->key; tlen = data->key_len; while (tlen != 0) { /* transmit the key in chunks of STARCOS_WKEY_CSIZE bytes */ u8 clen = tlen < STARCOS_WKEY_CSIZE ? tlen : STARCOS_WKEY_CSIZE; sbuf[0] = 0xc2; sbuf[1] = 3 + clen; sbuf[2] = data->kid; sbuf[3] = (offset >> 8) & 0xff; sbuf[4] = offset & 0xff; memcpy(sbuf+5, p, clen); len = 5 + clen; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xf4, data->mode, 0x00); apdu.cla |= 0x80; apdu.lc = len; apdu.datalen = len; apdu.data = sbuf; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); offset += clen; p += clen; tlen -= clen; } return SC_SUCCESS; } /** starcos_gen_key * generate public key pair * \param card pointer to the sc_card object * \param data pointer to a sc_starcos_gen_key_data structure * \return SC_SUCCESS or error code * * This function generates a public key pair and stores the created * private key in the ISF (specified by the KID). */ static int starcos_gen_key(sc_card_t *card, sc_starcos_gen_key_data *data) { int r; size_t i, len = data->key_length >> 3; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[2], *p, *q; CHECK_NOT_SUPPORTED_V3_4(card); /* generate key */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x46, 0x00, data->key_id); apdu.le = 0; sbuf[0] = (u8)(data->key_length >> 8); sbuf[1] = (u8)(data->key_length); apdu.data = sbuf; apdu.lc = 2; apdu.datalen = 2; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); /* read public key via READ PUBLIC KEY */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xf0, 0x9c, 0x00); sbuf[0] = data->key_id; apdu.cla |= 0x80; apdu.data = sbuf; apdu.datalen = 1; apdu.lc = 1; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return sc_check_sw(card, apdu.sw1, apdu.sw2); data->modulus = malloc(len); if (!data->modulus) return SC_ERROR_OUT_OF_MEMORY; p = data->modulus; /* XXX use tags to find starting position of the modulus */ q = &rbuf[18]; /* LSB to MSB -> MSB to LSB */ for (i = len; i != 0; i--) *p++ = q[i - 1]; return SC_SUCCESS; } /** starcos_set_security_env * sets the security environment * \param card pointer to the sc_card object * \param env pointer to a sc_security_env object * \param se_num not used here * \return SC_SUCCESS on success or an error code * * This function sets the security environment (using the starcos spk 2.3 * command MANAGE SECURITY ENVIRONMENT). In case a COMPUTE SIGNATURE * operation is requested , this function tries to detect whether * COMPUTE SIGNATURE or INTERNAL AUTHENTICATE must be used for signature * calculation. */ static int starcos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { u8 *p, *pp; int r, operation = env->operation; sc_apdu_t apdu; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; starcos_ex_data *ex_data = (starcos_ex_data *)card->drv_data; p = sbuf; if (card->type == SC_CARD_TYPE_STARCOS_V3_4 || card->type == SC_CARD_TYPE_STARCOS_V3_5) { if (!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) || !(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); } /* don't know what these mean but doesn't matter as card seems to take * algorithm / cipher from PKCS#1 padding prefix */ *p++ = 0x84; *p++ = 0x01; if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) { *p++ = *env->key_ref | 0x80; } else { *p++ = *env->key_ref; } switch (operation) { case SC_SEC_OPERATION_SIGN: sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB6); /* algorithm / cipher selector? */ *p++ = 0x89; *p++ = 0x02; *p++ = 0x13; *p++ = 0x23; break; case SC_SEC_OPERATION_DECIPHER: sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB8); /* algorithm / cipher selector? */ *p++ = 0x89; *p++ = 0x02; *p++ = 0x11; if (card->type == SC_CARD_TYPE_STARCOS_V3_4) *p++ = 0x30; else *p++ = 0x31; break; default: sc_log(card->ctx, "not supported for STARCOS 3.4 cards"); return SC_ERROR_NOT_SUPPORTED; } apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); if (env->algorithm_flags == SC_ALGORITHM_RSA_PAD_PKCS1) { // input data will be already padded ex_data->fix_digestInfo = 0; } else { ex_data->fix_digestInfo = env->algorithm_flags; } ex_data->sec_ops = SC_SEC_OPERATION_SIGN; return SC_SUCCESS; } /* copy key reference, if present */ if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) { if (env->flags & SC_SEC_ENV_KEY_REF_SYMMETRIC) *p++ = 0x83; else *p++ = 0x84; *p++ = env->key_ref_len; memcpy(p, env->key_ref, env->key_ref_len); p += env->key_ref_len; } pp = p; if (operation == SC_SEC_OPERATION_DECIPHER){ if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { *p++ = 0x80; *p++ = 0x01; *p++ = 0x02; } else return SC_ERROR_INVALID_ARGUMENTS; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x81, 0xb8); apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); return SC_SUCCESS; } /* try COMPUTE SIGNATURE */ if (operation == SC_SEC_OPERATION_SIGN && ( env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1 || env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796)) { if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { *p++ = 0x80; *p++ = 0x01; *p++ = env->algorithm_ref & 0xFF; } else if (env->flags & SC_SEC_ENV_ALG_PRESENT && env->algorithm == SC_ALGORITHM_RSA) { /* set the method to use based on the algorithm_flags */ *p++ = 0x80; *p++ = 0x01; if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) *p++ = 0x12; else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_RIPEMD160) *p++ = 0x22; else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5) *p++ = 0x32; else { /* can't use COMPUTE SIGNATURE => * try INTERNAL AUTHENTICATE */ p = pp; operation = SC_SEC_OPERATION_AUTHENTICATE; goto try_authenticate; } } else if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796) { if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) *p++ = 0x11; else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_RIPEMD160) *p++ = 0x21; else return SC_ERROR_INVALID_ARGUMENTS; } else return SC_ERROR_INVALID_ARGUMENTS; } sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xb6); apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; apdu.le = 0; /* we don't know whether to use * COMPUTE SIGNATURE or INTERNAL AUTHENTICATE */ r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { ex_data->fix_digestInfo = 0; ex_data->sec_ops = SC_SEC_OPERATION_SIGN; return SC_SUCCESS; } /* reset pointer */ p = pp; /* doesn't work => try next op */ operation = SC_SEC_OPERATION_AUTHENTICATE; } try_authenticate: /* try INTERNAL AUTHENTICATE */ if (operation == SC_SEC_OPERATION_AUTHENTICATE && env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { *p++ = 0x80; *p++ = 0x01; *p++ = 0x01; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xa4); apdu.data = sbuf; apdu.datalen = p - sbuf; apdu.lc = p - sbuf; apdu.le = 0; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); ex_data->fix_digestInfo = env->algorithm_flags; ex_data->sec_ops = SC_SEC_OPERATION_AUTHENTICATE; return SC_SUCCESS; } return SC_ERROR_INVALID_ARGUMENTS; } static int starcos_compute_signature(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { int r; sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; starcos_ex_data *ex_data = (starcos_ex_data *)card->drv_data; if (datalen > SC_MAX_APDU_BUFFER_SIZE) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); if (ex_data->sec_ops == SC_SEC_OPERATION_SIGN) { /* compute signature with the COMPUTE SIGNATURE command */ if (card->type == SC_CARD_TYPE_STARCOS_V3_4 || card->type == SC_CARD_TYPE_STARCOS_V3_5) { size_t tmp_len; sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 0; if (ex_data->fix_digestInfo) { // need to pad data unsigned int flags = ex_data->fix_digestInfo & SC_ALGORITHM_RSA_HASHES; if (flags == 0x00) { flags = SC_ALGORITHM_RSA_HASH_NONE; } tmp_len = sizeof(sbuf); r = sc_pkcs1_encode(card->ctx, flags, data, datalen, sbuf, &tmp_len, sizeof(sbuf)*8); LOG_TEST_RET(card->ctx, r, "sc_pkcs1_encode failed"); } else { memcpy(sbuf, data, datalen); tmp_len = datalen; } apdu.data = sbuf; apdu.datalen = tmp_len; apdu.lc = tmp_len; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 0; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); } else { /* set the hash value */ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x90, 0x81); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 0; memcpy(sbuf, data, datalen); apdu.data = sbuf; apdu.lc = datalen; apdu.datalen = datalen; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); /* call COMPUTE SIGNATURE */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x2A, 0x9E, 0x9A); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; apdu.lc = 0; apdu.datalen = 0; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); } if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); } } else if (ex_data->sec_ops == SC_SEC_OPERATION_AUTHENTICATE) { size_t tmp_len; CHECK_NOT_SUPPORTED_V3_4(card); /* call INTERNAL AUTHENTICATE */ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x10, 0x00); /* fix/create DigestInfo structure (if necessary) */ if (ex_data->fix_digestInfo) { unsigned int flags = ex_data->fix_digestInfo & SC_ALGORITHM_RSA_HASHES; if (flags == 0x0) /* XXX: assume no hash is wanted */ flags = SC_ALGORITHM_RSA_HASH_NONE; tmp_len = sizeof(sbuf); r = sc_pkcs1_encode(card->ctx, flags, data, datalen, sbuf, &tmp_len, sizeof(sbuf)*8); if (r < 0) return r; } else { memcpy(sbuf, data, datalen); tmp_len = datalen; } apdu.lc = tmp_len; apdu.data = sbuf; apdu.datalen = tmp_len; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); } } else SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); /* clear old state */ ex_data->sec_ops = 0; ex_data->fix_digestInfo = 0; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); } static int starcos_decipher(struct sc_card *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) { int r; size_t card_max_send_size = card->max_send_size; size_t reader_max_send_size = card->reader->max_send_size; size_t card_max_recv_size = card->max_recv_size; size_t reader_max_recv_size = card->reader->max_recv_size; if (sc_get_max_send_size(card) < crgram_len + 1) { /* Starcos doesn't support chaining for PSO:DEC, so we just _hope_ * that both, the reader and the card are able to send enough data. * (data is prefixed with 1 byte padding content indicator) */ card->max_send_size = crgram_len + 1; card->reader->max_send_size = crgram_len + 1; } if (sc_get_max_recv_size(card) < outlen) { /* Starcos doesn't support get response for PSO:DEC, so we just _hope_ * that both, the reader and the card are able to receive enough data. */ if (0 == (card->caps & SC_CARD_CAP_APDU_EXT) && outlen > 256) { card->max_recv_size = 256; card->reader->max_recv_size = 256; } else { card->max_recv_size = outlen; card->reader->max_recv_size = outlen; } } if (card->type == SC_CARD_TYPE_STARCOS_V3_4 || card->type == SC_CARD_TYPE_STARCOS_V3_5) { sc_apdu_t apdu; u8 *sbuf = malloc(crgram_len + 1); if (sbuf == NULL) return SC_ERROR_OUT_OF_MEMORY; sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x80, 0x86); apdu.resp = out; apdu.resplen = outlen; apdu.le = outlen; sbuf[0] = 0x81; memcpy(sbuf + 1, crgram, crgram_len); apdu.data = sbuf; apdu.lc = crgram_len + 1; apdu.datalen = crgram_len + 1; r = sc_transmit_apdu(card, &apdu); sc_mem_clear(sbuf, crgram_len + 1); free(sbuf); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) r = apdu.resplen; else r = sc_check_sw(card, apdu.sw1, apdu.sw2); } else { r = iso_ops->decipher(card, crgram, crgram_len, out, outlen); } /* reset whatever we've modified above */ card->max_send_size = card_max_send_size; card->reader->max_send_size = reader_max_send_size; card->max_recv_size = card_max_recv_size; card->reader->max_recv_size = reader_max_recv_size; LOG_FUNC_RETURN(card->ctx, r); } static int starcos_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) { const int err_count = sizeof(starcos_errors)/sizeof(starcos_errors[0]); int i; sc_log(card->ctx, "sw1 = 0x%02x, sw2 = 0x%02x\n", sw1, sw2); if (sw1 == 0x90) return SC_SUCCESS; if (sw1 == 0x63 && (sw2 & ~0x0fU) == 0xc0 ) { sc_log(card->ctx, "Verification failed (remaining tries: %d)\n", (sw2 & 0x0f)); return SC_ERROR_PIN_CODE_INCORRECT; } /* check starcos error messages */ for (i = 0; i < err_count; i++) if (starcos_errors[i].SWs == ((sw1 << 8) | sw2)) { sc_log(card->ctx, "%s\n", starcos_errors[i].errorstr); return starcos_errors[i].errorno; } /* iso error */ return iso_ops->check_sw(card, sw1, sw2); } static int starcos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { int r; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; sc_apdu_t apdu; if (!serial) return SC_ERROR_INVALID_ARGUMENTS; /* see if we have cached serial number */ if (card->serialnr.len) { memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } switch (card->type) { case SC_CARD_TYPE_STARCOS_V3_4: case SC_CARD_TYPE_STARCOS_V3_5: card->serialnr.len = SC_MAX_SERIALNR; r = sc_parse_ef_gdo(card, card->serialnr.value, &card->serialnr.len, NULL, 0); if (r < 0) { card->serialnr.len = 0; return r; } break; default: /* get serial number via GET CARD DATA */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xf6, 0x00, 0x00); apdu.cla |= 0x80; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 256; apdu.lc = 0; apdu.datalen = 0; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; /* cache serial number */ memcpy(card->serialnr.value, apdu.resp, MIN(apdu.resplen, SC_MAX_SERIALNR)); card->serialnr.len = MIN(apdu.resplen, SC_MAX_SERIALNR); break; } /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } static int starcos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { sc_starcos_create_data *tmp; switch (cmd) { case SC_CARDCTL_STARCOS_CREATE_FILE: tmp = (sc_starcos_create_data *) ptr; if (tmp->type == SC_STARCOS_MF_DATA) return starcos_create_mf(card, tmp); else if (tmp->type == SC_STARCOS_DF_DATA) return starcos_create_df(card, tmp); else if (tmp->type == SC_STARCOS_EF_DATA) return starcos_create_ef(card, tmp); else return SC_ERROR_INTERNAL; case SC_CARDCTL_STARCOS_CREATE_END: return starcos_create_end(card, (sc_file_t *)ptr); case SC_CARDCTL_STARCOS_WRITE_KEY: return starcos_write_key(card, (sc_starcos_wkey_data *)ptr); case SC_CARDCTL_STARCOS_GENERATE_KEY: return starcos_gen_key(card, (sc_starcos_gen_key_data *)ptr); case SC_CARDCTL_ERASE_CARD: return starcos_erase_card(card); case SC_CARDCTL_GET_SERIALNR: return starcos_get_serialnr(card, (sc_serial_number_t *)ptr); default: return SC_ERROR_NOT_SUPPORTED; } } static int starcos_logout(sc_card_t *card) { int r; sc_apdu_t apdu; const u8 mf_buf[2] = {0x3f, 0x00}; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x00, 0x0C); apdu.le = 0; apdu.lc = 2; apdu.data = mf_buf; apdu.datalen = 2; apdu.resplen = 0; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU re-transmit failed"); if (apdu.sw1 == 0x69 && apdu.sw2 == 0x85) /* the only possible reason for this error here is, afaik, * that no MF exists, but then there's no need to logout * => return SC_SUCCESS */ return SC_SUCCESS; return sc_check_sw(card, apdu.sw1, apdu.sw2); } static int starcos_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { int r; LOG_FUNC_CALLED(card->ctx); starcos_ex_data * ex_data = (starcos_ex_data*)card->drv_data; switch (card->type) { case SC_CARD_TYPE_STARCOS_V3_4: case SC_CARD_TYPE_STARCOS_V3_5: data->flags |= SC_PIN_CMD_NEED_PADDING; data->pin1.encoding = ex_data->pin_encoding; /* fall through */ default: r = iso_ops->pin_cmd(card, data, tries_left); } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static struct sc_card_driver * sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); if (iso_ops == NULL) iso_ops = iso_drv->ops; starcos_ops = *iso_drv->ops; starcos_ops.match_card = starcos_match_card; starcos_ops.init = starcos_init; starcos_ops.finish = starcos_finish; starcos_ops.select_file = starcos_select_file; starcos_ops.get_challenge = starcos_get_challenge; starcos_ops.check_sw = starcos_check_sw; starcos_ops.create_file = starcos_create_file; starcos_ops.delete_file = NULL; starcos_ops.set_security_env = starcos_set_security_env; starcos_ops.compute_signature = starcos_compute_signature; starcos_ops.decipher = starcos_decipher; starcos_ops.card_ctl = starcos_card_ctl; starcos_ops.logout = starcos_logout; starcos_ops.pin_cmd = starcos_pin_cmd; return &starcos_drv; } struct sc_card_driver * sc_get_starcos_driver(void) { return sc_get_driver(); }