/* * Initialize Cards according to PKCS#15. * * This is a fill in the blanks sort of exercise. You need a * profile that describes characteristics of your card, and the * application specific layout on the card. This program will * set up the card according to this specification (including * PIN initialization etc) and create the corresponding PKCS15 * structure. * * There are a very few tasks that are too card specific to have * a generic implementation; that is how PINs and keys are stored * on the card. These should be implemented in pkcs15-.c * * Copyright (C) 2002, Olaf Kirch * * 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 */ #include "config.h" #include #include #include #include #include #include #include #ifdef HAVE_GETTIMEOFDAY #include #endif #ifdef HAVE_STRINGS_H #include #endif #include #ifdef ENABLE_OPENSSL #include #include #include #include #include #include #include #endif #include "common/compat_strlcpy.h" #include "common/libscdl.h" #include "libopensc/pkcs15.h" #include "libopensc/cardctl.h" #include "libopensc/asn1.h" #include "libopensc/log.h" #include "profile.h" #include "pkcs15-init.h" #define OPENSC_INFO_FILEPATH "3F0050154946" #define OPENSC_INFO_FILEID 0x4946 #define OPENSC_INFO_TAG_PROFILE 0x01 #define OPENSC_INFO_TAG_OPTION 0x02 /* Default ID for new key/pin */ #define DEFAULT_ID 0x45 #define DEFAULT_PIN_FLAGS 0x03 #define DEFAULT_PRKEY_FLAGS 0x03 #define DEFAULT_PUBKEY_FLAGS 0x02 #define DEFAULT_CERT_FLAGS 0x02 #define DEFAULT_DATA_FLAGS 0x02 #define TEMPLATE_INSTANTIATE_MIN_INDEX 0x0 #define TEMPLATE_INSTANTIATE_MAX_INDEX 0xFE /* Maximal number of access conditions that can be defined for one card operation. */ #define SC_MAX_OP_ACS 16 /* Handle encoding of PKCS15 on the card */ typedef int (*pkcs15_encoder)(struct sc_context *, struct sc_pkcs15_card *, u8 **, size_t *); static int sc_pkcs15init_store_data(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *, struct sc_pkcs15_der *, struct sc_path *); static size_t sc_pkcs15init_keybits(struct sc_pkcs15_bignum *); static int sc_pkcs15init_update_dir(struct sc_pkcs15_card *, struct sc_profile *profile, struct sc_app_info *app); static int sc_pkcs15init_update_tokeninfo(struct sc_pkcs15_card *, struct sc_profile *profile); static int sc_pkcs15init_update_lastupdate(struct sc_pkcs15_card *, struct sc_profile *profile); static int sc_pkcs15init_update_odf(struct sc_pkcs15_card *, struct sc_profile *profile); static int sc_pkcs15init_map_usage(unsigned long, int); static int do_select_parent(struct sc_profile *, struct sc_pkcs15_card *, struct sc_file *, struct sc_file **); static int sc_pkcs15init_create_pin(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *, struct sc_pkcs15init_pinargs *); static int check_keygen_params_consistency(struct sc_card *card, struct sc_pkcs15init_keygen_args *args, unsigned int bits, unsigned int *out_bits); static int check_key_compatibility(struct sc_pkcs15_card *, struct sc_pkcs15_prkey *, unsigned int, unsigned int, unsigned int); static int prkey_fixup(struct sc_pkcs15_card *, struct sc_pkcs15_prkey *); static int prkey_bits(struct sc_pkcs15_card *, struct sc_pkcs15_prkey *); static int prkey_pkcs15_algo(struct sc_pkcs15_card *, struct sc_pkcs15_prkey *); static int select_intrinsic_id(struct sc_pkcs15_card *, struct sc_profile *, int, struct sc_pkcs15_id *, void *); static int select_id(struct sc_pkcs15_card *, int, struct sc_pkcs15_id *); static int select_object_path(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *, struct sc_path *); static int sc_pkcs15init_get_pin_path(struct sc_pkcs15_card *, struct sc_pkcs15_id *, struct sc_path *); static int sc_pkcs15init_qualify_pin(struct sc_card *, const char *, unsigned int, struct sc_pkcs15_auth_info *); static struct sc_pkcs15_df * find_df_by_type(struct sc_pkcs15_card *, unsigned int); static int sc_pkcs15init_read_info(struct sc_card *card, struct sc_profile *); static int sc_pkcs15init_parse_info(struct sc_card *, const unsigned char *, size_t, struct sc_profile *); static int sc_pkcs15init_write_info(struct sc_pkcs15_card *, struct sc_profile *, struct sc_pkcs15_object *); static struct profile_operations { const char *name; void *func; } profile_operations[] = { { "rutoken", (void *) sc_pkcs15init_get_rutoken_ops }, { "gpk", (void *) sc_pkcs15init_get_gpk_ops }, { "miocos", (void *) sc_pkcs15init_get_miocos_ops }, { "flex", (void *) sc_pkcs15init_get_cryptoflex_ops }, { "cyberflex", (void *) sc_pkcs15init_get_cyberflex_ops }, { "cardos", (void *) sc_pkcs15init_get_cardos_ops }, { "etoken", (void *) sc_pkcs15init_get_cardos_ops }, /* legacy */ { "jcop", (void *) sc_pkcs15init_get_jcop_ops }, { "starcos", (void *) sc_pkcs15init_get_starcos_ops }, { "oberthur", (void *) sc_pkcs15init_get_oberthur_ops }, { "openpgp", (void *) sc_pkcs15init_get_openpgp_ops }, { "setcos", (void *) sc_pkcs15init_get_setcos_ops }, { "incrypto34", (void *) sc_pkcs15init_get_incrypto34_ops }, { "muscle", (void*) sc_pkcs15init_get_muscle_ops }, { "asepcos", (void*) sc_pkcs15init_get_asepcos_ops }, { "entersafe",(void*) sc_pkcs15init_get_entersafe_ops }, { "epass2003",(void*) sc_pkcs15init_get_epass2003_ops }, { "rutoken_ecp", (void *) sc_pkcs15init_get_rtecp_ops }, { "westcos", (void *) sc_pkcs15init_get_westcos_ops }, { "myeid", (void *) sc_pkcs15init_get_myeid_ops }, { "sc-hsm", (void *) sc_pkcs15init_get_sc_hsm_ops }, #ifdef ENABLE_OPENSSL { "authentic", (void *) sc_pkcs15init_get_authentic_ops }, { "iasecc", (void *) sc_pkcs15init_get_iasecc_ops }, #endif { NULL, NULL }, }; static struct sc_pkcs15init_callbacks callbacks = { NULL, NULL, }; static void sc_pkcs15init_empty_callback(void *ptr) { } /* * Set the application callbacks */ void sc_pkcs15init_set_callbacks(struct sc_pkcs15init_callbacks *cb) { callbacks.get_pin = cb? cb->get_pin : NULL; callbacks.get_key = cb? cb->get_key : NULL; } /* * Returns 1 if the a profile was found in the card's card_driver block * in the config file, or 0 otherwise. */ static int get_profile_from_config(struct sc_card *card, char *buffer, size_t size) { struct sc_context *ctx = card->ctx; const char *tmp; scconf_block **blocks, *blk; int i; for (i = 0; ctx->conf_blocks[i]; i++) { blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "card_driver", card->driver->short_name); blk = blocks[0]; free(blocks); if (blk == NULL) continue; tmp = scconf_get_str(blk, "profile", NULL); if (tmp != NULL) { strlcpy(buffer, tmp, size); return 1; } } return 0; } static const char * find_library(struct sc_context *ctx, const char *name) { int i; const char *libname = NULL; scconf_block *blk, **blocks; for (i = 0; ctx->conf_blocks[i]; i++) { blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], "framework", "pkcs15"); blk = blocks[0]; free(blocks); if (blk == NULL) continue; blocks = scconf_find_blocks(ctx->conf, blk, "pkcs15init", name); blk = blocks[0]; free(blocks); if (blk == NULL) continue; libname = scconf_get_str(blk, "module", NULL); break; } if (!libname) { sc_log(ctx, "unable to locate pkcs15init driver for '%s'", name); } return libname; } static void * load_dynamic_driver(struct sc_context *ctx, void **dll, const char *name) { const char *version, *libname; void *handle; void *(*modinit)(const char *) = NULL; const char *(*modversion)(void) = NULL; libname = find_library(ctx, name); if (!libname) return NULL; handle = sc_dlopen(libname); if (handle == NULL) { sc_log(ctx, "Module %s: cannot load '%s' library: %s", name, libname, sc_dlerror()); return NULL; } /* verify correctness of module */ modinit = (void *(*)(const char *)) sc_dlsym(handle, "sc_module_init"); modversion = (const char *(*)(void)) sc_dlsym(handle, "sc_driver_version"); if (modinit == NULL || modversion == NULL) { sc_log(ctx, "dynamic library '%s' is not a OpenSC module",libname); sc_dlclose(handle); return NULL; } /* verify module version */ version = modversion(); if (version == NULL || strncmp(version, "0.9.", strlen("0.9.")) > 0) { sc_log(ctx,"dynamic library '%s': invalid module version",libname); sc_dlclose(handle); return NULL; } *dll = handle; sc_log(ctx, "successfully loaded pkcs15init driver '%s'", name); return modinit(name); } /* * Set up profile */ int sc_pkcs15init_bind(struct sc_card *card, const char *name, const char *profile_option, struct sc_app_info *app_info, struct sc_profile **result) { struct sc_context *ctx = card->ctx; struct sc_profile *profile; struct sc_pkcs15init_operations * (* func)(void) = NULL; const char *driver = card->driver->short_name; char card_profile[PATH_MAX]; int r, i; LOG_FUNC_CALLED(ctx); /* Put the card into administrative mode */ r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) LOG_TEST_RET(ctx, r, "Set lifecycle error"); profile = sc_profile_new(); profile->card = card; for (i = 0; profile_operations[i].name; i++) { if (!strcasecmp(driver, profile_operations[i].name)) { func = (struct sc_pkcs15init_operations *(*)(void)) profile_operations[i].func; break; } } if (!func) { /* no builtin support for this driver => look if there's a * dynamic module for this card */ func = (struct sc_pkcs15init_operations *(*)(void)) load_dynamic_driver(card->ctx, &profile->dll, driver); } if (func) { profile->ops = func(); } else { sc_log(ctx, "Unsupported card driver %s", driver); sc_profile_free(profile); LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported card driver"); } /* Massage the main profile name to see if there are * any options in there */ profile->name = strdup(name); if (strchr(profile->name, '+') != NULL) { char *s; i = 0; (void) strtok(profile->name, "+"); while ((s = strtok(NULL, "+")) != NULL) { if (i < SC_PKCS15INIT_MAX_OPTIONS-1) profile->options[i++] = strdup(s); } } r = sc_pkcs15init_read_info(card, profile); if (r < 0) { sc_profile_free(profile); LOG_TEST_RET(ctx, r, "Read info error"); } /* Check the config file for a profile name. * If none is defined, use the default profile name. */ if (!get_profile_from_config(card, card_profile, sizeof(card_profile))) strcpy(card_profile, driver); if (profile_option != NULL) strlcpy(card_profile, profile_option, sizeof(card_profile)); do { r = sc_profile_load(profile, profile->name); if (r < 0) { sc_log(ctx, "Failed to load profile '%s': %s", profile->name, sc_strerror(r)); break; } r = sc_profile_load(profile, card_profile); if (r < 0) { sc_log(ctx, "Failed to load profile '%s': %s", card_profile, sc_strerror(r)); break; } r = sc_profile_finish(profile, app_info); if (r < 0) sc_log(ctx, "Failed to finalize profile: %s", sc_strerror(r)); } while (0); if (r < 0) { sc_profile_free(profile); LOG_TEST_RET(ctx, r, "Load profile error"); } if (app_info && app_info->aid.len) { struct sc_path path; if (card->ef_atr && card->ef_atr->aid.len) { sc_log(ctx, "sc_pkcs15init_bind() select MF using EF.ATR data"); memset(&path, 0, sizeof(struct sc_path)); path.type = SC_PATH_TYPE_DF_NAME; path.aid = card->ef_atr->aid; r = sc_select_file(card, &path, NULL); if (r) return r; } if (app_info->path.len) { path = app_info->path; } else { memset(&path, 0, sizeof(struct sc_path)); path.type = SC_PATH_TYPE_DF_NAME; path.aid = app_info->aid; } sc_log(ctx, "sc_pkcs15init_bind() select application path(type:%X) '%s'", path.type, sc_print_path(&path)); r = sc_select_file(card, &path, NULL); } *result = profile; LOG_FUNC_RETURN(ctx, r); } void sc_pkcs15init_unbind(struct sc_profile *profile) { int r; struct sc_context *ctx = profile->card->ctx; LOG_FUNC_CALLED(ctx); sc_log(ctx, "Pksc15init Unbind: %i:%p:%i", profile->dirty, profile->p15_data, profile->pkcs15.do_last_update); if (profile->dirty != 0 && profile->p15_data != NULL && profile->pkcs15.do_last_update) { r = sc_pkcs15init_update_lastupdate(profile->p15_data, profile); if (r < 0) sc_log(ctx, "Failed to update TokenInfo: %s", sc_strerror(r)); } if (profile->dll) sc_dlclose(profile->dll); sc_profile_free(profile); } void sc_pkcs15init_set_p15card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *p15objects[10]; int i, r, nn_objs; LOG_FUNC_CALLED(ctx); /* Prepare pin-domain instantiation: * for every present local User PIN, add to the profile EF list the named PIN path. */ nn_objs = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, p15objects, 10); for (i = 0; i < nn_objs; i++) { struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *) p15objects[i]->data; struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin; struct sc_file *file = NULL; if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) continue; if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) continue; if (!auth_info->path.len) continue; r = sc_profile_get_file_by_path(profile, &auth_info->path, &file); if (r == SC_ERROR_FILE_NOT_FOUND) { if (!sc_select_file(p15card->card, &auth_info->path, &file)) { char pin_name[16]; sprintf(pin_name, "pin-dir-%02X%02X", file->path.value[file->path.len - 2], file->path.value[file->path.len - 1]); sc_log(ctx, "add '%s' to profile file list", pin_name); sc_profile_add_file(profile, pin_name, file); } } if (file) sc_file_free(file); } profile->p15_data = p15card; sc_log(ctx, "sc_pkcs15init_set_p15card() returns"); } /* * Set the card's lifecycle */ int sc_pkcs15init_set_lifecycle(struct sc_card *card, int lcycle) { return sc_card_ctl(card, SC_CARDCTL_LIFECYCLE_SET, &lcycle); } /* * Erase the card */ int sc_pkcs15init_erase_card(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_aid *aid) { struct sc_context *ctx = p15card->card->ctx; int rv; LOG_FUNC_CALLED(ctx); /* Needs the 'SOPIN' AUTH pkcs15 object. * So that, SOPIN can be found by it's reference. */ if (sc_pkcs15_bind(p15card->card, aid, &p15card) >= 0) profile->p15_data = p15card; if (profile->ops->erase_card == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); rv = profile->ops->erase_card(profile, p15card); LOG_FUNC_RETURN(ctx, rv); } int sc_pkcs15init_erase_card_recursively(struct sc_pkcs15_card *p15card, struct sc_profile *profile) { struct sc_file *df = profile->df_info->file, *dir; int r; /* Delete EF(DIR). This may not be very nice * against other applications that use this file, but * extremely useful for testing :) * Note we need to delete it before the DF because we create * it *after* the DF. Some cards (e.g. the cryptoflex) want * us to delete files in reverse order of creation. * */ if (sc_profile_get_file(profile, "DIR", &dir) >= 0) { r = sc_pkcs15init_rmdir(p15card, profile, dir); sc_file_free(dir); if (r < 0 && r != SC_ERROR_FILE_NOT_FOUND) { sc_free_apps(p15card->card); return r; } } r = sc_select_file(p15card->card, &df->path, &df); if (r >= 0) { r = sc_pkcs15init_rmdir(p15card, profile, df); sc_file_free(df); } if (r == SC_ERROR_FILE_NOT_FOUND) r = 0; sc_free_apps(p15card->card); return r; } int sc_pkcs15init_delete_by_path(struct sc_profile *profile, struct sc_pkcs15_card *p15card, const struct sc_path *file_path) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *parent = NULL, *file = NULL; struct sc_path path; int rv, file_type = SC_FILE_TYPE_DF; LOG_FUNC_CALLED(ctx); sc_log(ctx, "trying to delete '%s'", sc_print_path(file_path)); /* For some cards, to delete file should be satisfied the 'DELETE' ACL of the file itself, * for the others the 'DELETE' ACL of parent. * Let's start from the file's 'DELETE' ACL. * * TODO: 'DELETE_SELF' exists. Proper solution would be to use this acl by every * card (driver and profile) that uses self delete ACL. */ /* Select the file itself */ path = *file_path; rv = sc_select_file(p15card->card, &path, &file); LOG_TEST_RET(ctx, rv, "cannot select file to delete"); if (sc_file_get_acl_entry(file, SC_AC_OP_DELETE_SELF)) { sc_log(ctx, "Found 'DELETE-SELF' acl"); rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_DELETE_SELF); sc_file_free(file); } else if (sc_file_get_acl_entry(file, SC_AC_OP_DELETE)) { sc_log(ctx, "Found 'DELETE' acl"); rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_DELETE); sc_file_free(file); } else { sc_log(ctx, "Try to get the parent's 'DELETE' access"); file_type = file->type; if (file_path->len >= 2) { /* Select the parent DF */ path.len -= 2; rv = sc_select_file(p15card->card, &path, &parent); LOG_TEST_RET(ctx, rv, "Cannot select parent"); rv = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_DELETE); sc_file_free(parent); LOG_TEST_RET(ctx, rv, "parent 'DELETE' authentication failed"); } } LOG_TEST_RET(ctx, rv, "'DELETE' authentication failed"); memset(&path, 0, sizeof(path)); path.type = SC_PATH_TYPE_FILE_ID; path.value[0] = file_path->value[file_path->len - 2]; path.value[1] = file_path->value[file_path->len - 1]; path.len = 2; /* Reselect file to delete if the parent DF was selected and it's not DF. */ if (file_type != SC_FILE_TYPE_DF) { rv = sc_select_file(p15card->card, &path, &file); LOG_TEST_RET(ctx, rv, "cannot select file to delete"); } sc_log(ctx, "Now really delete file"); rv = sc_delete_file(p15card->card, &path); LOG_FUNC_RETURN(ctx, rv); } /* * Try to delete a file (and, in the DF case, its contents). * Note that this will not work if a pkcs#15 file's ERASE AC * references a pin other than the SO pin. */ int sc_pkcs15init_rmdir(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *df) { struct sc_context *ctx = p15card->card->ctx; unsigned char buffer[1024]; struct sc_path path; struct sc_file *file, *parent; int r = 0, nfids; if (df == NULL) return SC_ERROR_INTERNAL; sc_log(ctx, "sc_pkcs15init_rmdir(%s)", sc_print_path(&df->path)); if (df->type == SC_FILE_TYPE_DF) { r = sc_pkcs15init_authenticate(profile, p15card, df, SC_AC_OP_LIST_FILES); if (r < 0) return r; r = sc_list_files(p15card->card, buffer, sizeof(buffer)); if (r < 0) return r; path = df->path; path.len += 2; nfids = r / 2; while (r >= 0 && nfids--) { path.value[path.len-2] = buffer[2*nfids]; path.value[path.len-1] = buffer[2*nfids+1]; r = sc_select_file(p15card->card, &path, &file); if (r < 0) { if (r == SC_ERROR_FILE_NOT_FOUND) continue; break; } r = sc_pkcs15init_rmdir(p15card, profile, file); sc_file_free(file); } if (r < 0) return r; } /* Select the parent DF */ path = df->path; path.len -= 2; r = sc_select_file(p15card->card, &path, &parent); if (r < 0) return r; r = sc_pkcs15init_authenticate(profile, p15card, df, SC_AC_OP_DELETE); if (r < 0) { sc_file_free(parent); return r; } r = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_DELETE); sc_file_free(parent); if (r < 0) return r; memset(&path, 0, sizeof(path)); path.type = SC_PATH_TYPE_FILE_ID; path.value[0] = df->id >> 8; path.value[1] = df->id & 0xFF; path.len = 2; /* ensure that the card is in the correct lifecycle */ r = sc_pkcs15init_set_lifecycle(p15card->card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) return r; r = sc_delete_file(p15card->card, &path); return r; } int sc_pkcs15init_finalize_card(struct sc_card *card, struct sc_profile *profile) { if (profile->ops->finalize_card == NULL) return SC_ERROR_NOT_SUPPORTED; return profile->ops->finalize_card(card); } int sc_pkcs15init_finalize_profile(struct sc_card *card, struct sc_profile *profile, struct sc_aid *aid) { struct sc_context *ctx = card->ctx; const struct sc_app_info *app = NULL; int rv; LOG_FUNC_CALLED(ctx); if (card->app_count < 0) sc_enum_apps(card); if (aid) { sc_log(ctx, "finalize profile for AID %s", sc_dump_hex(aid->value, aid->len)); app = sc_find_app(card, aid); if (!app) { sc_log(ctx, "Cannot find oncard application"); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } } else if (card->app_count == 1) { app = card->app[0]; } else if (card->app_count > 1) { LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Need AID defined in this context"); } sc_log(ctx, "Finalize profile with application '%s'", app ? app->label : "default"); rv = sc_profile_finish(profile, app); sc_log(ctx, "sc_pkcs15init_finalize_profile() returns %i", rv); LOG_FUNC_RETURN(ctx, rv); } /* * Initialize the PKCS#15 application */ int sc_pkcs15init_add_app(struct sc_card *card, struct sc_profile *profile, struct sc_pkcs15init_initargs *args) { struct sc_context *ctx = card->ctx; struct sc_pkcs15_card *p15card = profile->p15_spec; struct sc_pkcs15_auth_info pin_ainfo, puk_ainfo; struct sc_pkcs15_pin_attributes *pin_attrs = &pin_ainfo.attrs.pin; struct sc_pkcs15_object *pin_obj = NULL; struct sc_app_info *app; struct sc_file *df = profile->df_info->file; int r; LOG_FUNC_CALLED(ctx); p15card->card = card; /* FIXME: * Some cards need pincache * for ex. to create temporary CHV key with the value of default AUTH key. */ p15card->opts.use_pin_cache = 1; if (card->app_count >= SC_MAX_CARD_APPS) LOG_TEST_RET(ctx, SC_ERROR_TOO_MANY_OBJECTS, "Too many applications on this card."); /* If the profile requires an SO PIN, check min/max length */ if (args->so_pin_len) { const char *pin_label; sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &pin_ainfo); r = sc_pkcs15init_qualify_pin(card, "SO PIN", args->so_pin_len, &pin_ainfo); LOG_TEST_RET(ctx, r, "Failed to qualify SO PIN"); /* Path encoded only for local SO PIN */ if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_LOCAL) pin_ainfo.path = df->path; /* Select the PIN reference */ if (profile->ops->select_pin_reference) { r = profile->ops->select_pin_reference(profile, p15card, &pin_ainfo); LOG_TEST_RET(ctx, r, "Failed to select card specific PIN reference"); } sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PUK, &puk_ainfo); r = sc_pkcs15init_qualify_pin(card, "SO PUK", args->so_puk_len, &puk_ainfo); LOG_TEST_RET(ctx, r, "Failed to qulify SO PUK"); if (!(pin_label = args->so_pin_label)) { if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) pin_label = "Security Officer PIN"; else pin_label = "User PIN"; } if (args->so_puk_len == 0) pin_attrs->flags |= SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED; pin_obj = sc_pkcs15init_new_object(SC_PKCS15_TYPE_AUTH_PIN, pin_label, NULL, &pin_ainfo); if (pin_obj) { /* When composing ACLs to create 'DIR' DF, * the references of the not-yet-existing PINs can be requested. * For this, create a 'virtual' AUTH object 'SO PIN', accessible by the card specific part, * but not yet written into the on-card PKCS#15. */ sc_log(ctx, "Add virtual SO_PIN('%s',flags:%X,reference:%i,path:'%s')", pin_obj->label, pin_attrs->flags, pin_attrs->reference, sc_print_path(&pin_ainfo.path)); r = sc_pkcs15_add_object(p15card, pin_obj); LOG_TEST_RET(ctx, r, "Failed to add 'SOPIN' AUTH object"); } } /* Perform card-specific initialization */ if (profile->ops->init_card) { r = profile->ops->init_card(profile, p15card); if (r < 0 && pin_obj) { sc_pkcs15_remove_object(p15card, pin_obj); sc_pkcs15_free_object(pin_obj); } LOG_TEST_RET(ctx, r, "Card specific init failed"); } /* Create the application directory */ r = profile->ops->create_dir(profile, p15card, df); LOG_TEST_RET(ctx, r, "Create 'DIR' error"); /* Store SO PIN */ if (pin_obj) r = profile->ops->create_pin(profile, p15card, df, pin_obj, args->so_pin, args->so_pin_len, args->so_puk, args->so_puk_len); #if 0 if (r > 0 && profile->ops->finalize_dir) r = profile->ops->finalize_dir(profile, p15card); #endif if (pin_obj) /* Remove 'virtual' AUTH object . */ sc_pkcs15_remove_object(p15card, pin_obj); if (r < 0) sc_pkcs15_free_object(pin_obj); LOG_TEST_RET(ctx, r, "Card specific create application DF failed"); /* Store the PKCS15 information on the card * We cannot use sc_pkcs15_create() because it makes * all sorts of assumptions about DF and EF names, and * doesn't work if secure messaging is required for the * MF (which is the case with the GPK) */ app = (struct sc_app_info *)calloc(1, sizeof(*app)); if (app == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Failed to allocate application info"); app->path = p15card->file_app->path; if (p15card->file_app->namelen <= SC_MAX_AID_SIZE) { app->aid.len = p15card->file_app->namelen; memcpy(app->aid.value, p15card->file_app->name, app->aid.len); } /* set serial number if explicitly specified */ if (args->serial) { sc_pkcs15init_set_serial(profile, args->serial); } else { /* otherwise try to get the serial number from the card */ struct sc_serial_number serialnr; r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serialnr); if (r == SC_SUCCESS) { char hex_serial[SC_MAX_SERIALNR * 2 + 1]; sc_bin_to_hex(serialnr.value, serialnr.len, hex_serial, sizeof(hex_serial), 0); sc_pkcs15init_set_serial(profile, hex_serial); } } if (args->label) { if (p15card->tokeninfo->label) free(p15card->tokeninfo->label); p15card->tokeninfo->label = strdup(args->label); } app->label = strdup(p15card->tokeninfo->label); /* See if we've set an SO PIN */ r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_AODF, pin_obj); if (r >= 0) { r = sc_pkcs15init_update_dir(p15card, profile, app); if (r >= 0) r = sc_pkcs15init_update_tokeninfo(p15card, profile); /* FIXME: what to do if sc_pkcs15init_update_dir failed? */ } else { free(app); /* unused */ } sc_pkcs15init_write_info(p15card, profile, pin_obj); LOG_FUNC_RETURN(ctx, r); } /* * Store a PIN/PUK pair */ static int sc_pkcs15init_store_puk(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_pinargs *args) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *pin_obj; struct sc_pkcs15_auth_info *auth_info; int r; char puk_label[0x30]; LOG_FUNC_CALLED(ctx); if (!args->puk_id.len) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "PUK auth ID not supplied"); /* Make sure we don't get duplicate PIN IDs */ r = sc_pkcs15_find_pin_by_auth_id(p15card, &args->puk_id, NULL); if (r != SC_ERROR_OBJECT_NOT_FOUND) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "There already is a PIN with this ID."); if (!args->puk_label) { if (args->label) snprintf(puk_label, sizeof(puk_label), "%s (PUK)", args->label); else snprintf(puk_label, sizeof(puk_label), "User PUK"); args->puk_label = puk_label; } args->pin = args->puk; args->pin_len = args->puk_len; args->puk = NULL; args->puk_len = 0; pin_obj = sc_pkcs15init_new_object(SC_PKCS15_TYPE_AUTH_PIN, args->puk_label, NULL, NULL); if (pin_obj == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate PIN object"); auth_info = (struct sc_pkcs15_auth_info *) pin_obj->data; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, auth_info); auth_info->auth_id = args->puk_id; /* Now store the PINs */ if (profile->ops->create_pin) r = sc_pkcs15init_create_pin(p15card, profile, pin_obj, args); else LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "In Old API store PUK object is not supported"); if (r >= 0) r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_AODF, pin_obj); else sc_pkcs15_free_object(pin_obj); profile->dirty = 1; LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15init_store_pin(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_pinargs *args) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *pin_obj; struct sc_pkcs15_auth_info *auth_info; int r; LOG_FUNC_CALLED(ctx); /* No auth_id given: select one */ if (args->auth_id.len == 0) { unsigned int n; args->auth_id.len = 1; for (n = 1, r = 0; n < 256; n++) { args->auth_id.value[0] = n; r = sc_pkcs15_find_pin_by_auth_id(p15card, &args->auth_id, NULL); if (r == SC_ERROR_OBJECT_NOT_FOUND) break; } if (r != SC_ERROR_OBJECT_NOT_FOUND) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "No auth_id specified for new PIN"); } else { /* Make sure we don't get duplicate PIN IDs */ r = sc_pkcs15_find_pin_by_auth_id(p15card, &args->auth_id, NULL); if (r != SC_ERROR_OBJECT_NOT_FOUND) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "There already is a PIN with this ID."); } pin_obj = sc_pkcs15init_new_object(SC_PKCS15_TYPE_AUTH_PIN, args->label, NULL, NULL); if (pin_obj == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate PIN object"); auth_info = (struct sc_pkcs15_auth_info *) pin_obj->data; sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, auth_info); auth_info->auth_id = args->auth_id; /* Now store the PINs */ sc_log(ctx, "Store PIN(%s,authID:%s)", pin_obj->label, sc_pkcs15_print_id(&auth_info->auth_id)); r = sc_pkcs15init_create_pin(p15card, profile, pin_obj, args); if (r < 0) sc_pkcs15_free_object(pin_obj); LOG_TEST_RET(ctx, r, "Card specific create PIN failed."); r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_AODF, pin_obj); if (r < 0) sc_pkcs15_free_object(pin_obj); LOG_TEST_RET(ctx, r, "Failed to add PIN object"); if (args->puk_id.len) r = sc_pkcs15init_store_puk(p15card, profile, args); profile->dirty = 1; LOG_FUNC_RETURN(ctx, r); } static int sc_pkcs15init_create_pin(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *pin_obj, struct sc_pkcs15init_pinargs *args) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *) pin_obj->data; struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin; struct sc_file *df = profile->df_info->file; int r, retry = 0; LOG_FUNC_CALLED(ctx); /* Some cards need to keep all their PINs in separate directories. * Create a subdirectory now, and put the pin into * this subdirectory */ if (profile->pin_domains) { if (!profile->ops->create_domain) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PIN domains not supported."); r = profile->ops->create_domain(profile, p15card, &auth_info->auth_id, &df); LOG_TEST_RET(ctx, r, "Card specific create domain failed"); } /* Path encoded only for local PINs */ if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_LOCAL) auth_info->path = df->path; /* pin_info->reference = 0; */ /* Loop until we come up with an acceptable pin reference */ while (1) { if (profile->ops->select_pin_reference) { r = profile->ops->select_pin_reference(profile, p15card, auth_info); LOG_TEST_RET(ctx, r, "Card specific select PIN reference failed"); retry = 1; } r = sc_pkcs15_find_pin_by_reference(p15card, &auth_info->path, pin_attrs->reference, NULL); if (r == SC_ERROR_OBJECT_NOT_FOUND) break; if (r != 0 || !retry) /* Other error trying to retrieve pin obj */ LOG_TEST_RET(ctx, SC_ERROR_TOO_MANY_OBJECTS, "Failed to allocate PIN reference."); pin_attrs->reference++; } if (args->puk_len == 0) pin_attrs->flags |= SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED; sc_log(ctx, "create PIN with reference:%X, flags:%X, path:%s", pin_attrs->reference, pin_attrs->flags, sc_print_path(&auth_info->path)); r = profile->ops->create_pin(profile, p15card, df, pin_obj, args->pin, args->pin_len, args->puk, args->puk_len); if (df != profile->df_info->file) sc_file_free(df); LOG_FUNC_RETURN(ctx, r); } /* * Default function for creating a pin subdirectory */ int sc_pkcs15_create_pin_domain(struct sc_profile *profile, struct sc_pkcs15_card *p15card, const struct sc_pkcs15_id *id, struct sc_file **ret) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *df = profile->df_info->file; int r; sc_log(ctx, "create PIN domain (path:%s,ID:%s)", sc_print_path(&df->path), sc_pkcs15_print_id(id)); /* Instantiate PIN directory just below the application DF */ r = sc_profile_instantiate_template(profile, "pin-domain", &df->path, "pin-dir", id, ret); if (r >= 0) { sc_log(ctx, "create PIN DF(path:%s)", sc_print_path(&(*ret)->path)); r = profile->ops->create_dir(profile, p15card, *ret); } return r; } static int sc_pkcs15init_encode_prvkey_content(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey *prvkey, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; LOG_FUNC_CALLED(ctx); if (prvkey->algorithm == SC_ALGORITHM_RSA) { struct sc_pkcs15_pubkey pubkey; int rv; pubkey.algorithm = prvkey->algorithm; pubkey.u.rsa.modulus = prvkey->u.rsa.modulus; pubkey.u.rsa.exponent = prvkey->u.rsa.exponent; rv = sc_pkcs15_encode_pubkey(ctx, &pubkey, &object->content.value, &object->content.len); LOG_TEST_RET(ctx, rv, "Failed to encode public key"); } LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Prepare private key download, and initialize a prkdf entry */ static int sc_pkcs15init_init_prkdf(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_prkeyargs *keyargs, struct sc_pkcs15_prkey *key, int keybits, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_prkey_info *key_info; struct sc_pkcs15_keyinfo_gostparams *keyinfo_gostparams; struct sc_pkcs15_object *object; const char *label; unsigned int usage; int r = 0, key_type; LOG_FUNC_CALLED(ctx); if (!res_obj || !keybits) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Initialize PrKDF entry failed"); *res_obj = NULL; if ((usage = keyargs->usage) == 0) { usage = SC_PKCS15_PRKEY_USAGE_SIGN; if (keyargs->x509_usage) usage = sc_pkcs15init_map_usage(keyargs->x509_usage, 1); } if ((label = keyargs->label) == NULL) label = "Private Key"; /* Create the prkey object now. * If we find out below that we're better off reusing an * existing object, we'll ditch this one */ key_type = prkey_pkcs15_algo(p15card, key); LOG_TEST_RET(ctx, key_type, "Unsupported key type"); object = sc_pkcs15init_new_object(key_type, label, &keyargs->auth_id, NULL); if (object == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate new PrKey object"); key_info = (struct sc_pkcs15_prkey_info *) object->data; key_info->usage = usage; key_info->native = 1; key_info->key_reference = 0; key_info->modulus_length = keybits; key_info->access_flags = keyargs->access_flags; /* Path is selected below */ if (keyargs->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE) { key_info->access_flags &= ~SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; key_info->native = 0; } /* Select a Key ID if the user didn't specify one, * otherwise make sure it's compatible with our intended use */ r = select_id(p15card, SC_PKCS15_TYPE_PRKEY, &keyargs->id); LOG_TEST_RET(ctx, r, "Cannot select ID for PrKey object"); key_info->id = keyargs->id; if (key->algorithm == SC_ALGORITHM_GOSTR3410) { key_info->params.len = sizeof(*keyinfo_gostparams); /* FIXME: malloc() call in pkcs15init, but free() call * in libopensc (sc_pkcs15_free_prkey_info) */ key_info->params.data = malloc(key_info->params.len); if (!key_info->params.data) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate memory for GOST parameters"); keyinfo_gostparams = key_info->params.data; keyinfo_gostparams->gostr3410 = keyargs->params.gost.gostr3410; keyinfo_gostparams->gostr3411 = keyargs->params.gost.gostr3411; keyinfo_gostparams->gost28147 = keyargs->params.gost.gost28147; } else if (key->algorithm == SC_ALGORITHM_EC) { struct sc_pkcs15_ec_parameters *ecparams = &keyargs->params.ec; key_info->params.data = &keyargs->params.ec; key_info->params.free_params = sc_pkcs15init_empty_callback; key_info->field_length = ecparams->field_length; } r = select_object_path(p15card, profile, object, &key_info->path); LOG_TEST_RET(ctx, r, "Failed to select private key object path"); /* See if we need to select a key reference for this object */ if (profile->ops->select_key_reference) { while (1) { sc_log(ctx, "Look for usable key reference starting from %i", key_info->key_reference); r = profile->ops->select_key_reference(profile, p15card, key_info); LOG_TEST_RET(ctx, r, "Failed to select card specific key reference"); r = sc_pkcs15_find_prkey_by_reference(p15card, &key_info->path, key_info->key_reference, NULL); if (r == SC_ERROR_OBJECT_NOT_FOUND) { sc_log(ctx, "Will use key reference %i", key_info->key_reference); break; } if (r != 0) /* Other error trying to retrieve pin obj */ LOG_TEST_RET(ctx, SC_ERROR_TOO_MANY_OBJECTS, "Failed to select key reference"); key_info->key_reference++; } } *res_obj = object; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Generate a new private key */ int sc_pkcs15init_generate_key(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_keygen_args *keygen_args, unsigned int keybits, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15init_pubkeyargs pubkey_args; struct sc_pkcs15_object *object; struct sc_pkcs15_prkey_info *key_info; int r, caller_supplied_id = 0; LOG_FUNC_CALLED(ctx); /* check supported key size */ r = check_keygen_params_consistency(p15card->card, keygen_args, keybits, &keybits); LOG_TEST_RET(ctx, r, "Invalid key size"); if (check_key_compatibility(p15card, &keygen_args->prkey_args.key, keygen_args->prkey_args.x509_usage, keybits, SC_ALGORITHM_ONBOARD_KEY_GEN)) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Cannot generate key with the given parameters"); if (profile->ops->generate_key == NULL) LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Key generation not supported"); if (keygen_args->prkey_args.id.len) { caller_supplied_id = 1; /* Make sure that private key's ID is the unique inside the PKCS#15 application */ r = sc_pkcs15_find_prkey_by_id(p15card, &keygen_args->prkey_args.id, NULL); if (!r) LOG_TEST_RET(ctx, SC_ERROR_NON_UNIQUE_ID, "Non unique ID of the private key object"); else if (r != SC_ERROR_OBJECT_NOT_FOUND) LOG_TEST_RET(ctx, r, "Find private key error"); } /* Set up the PrKDF object */ r = sc_pkcs15init_init_prkdf(p15card, profile, &keygen_args->prkey_args, &keygen_args->prkey_args.key, keybits, &object); LOG_TEST_RET(ctx, r, "Set up private key object error"); key_info = (struct sc_pkcs15_prkey_info *) object->data; if (keygen_args->prkey_args.guid) { object->guid = strdup(keygen_args->prkey_args.guid); if (!object->guid) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate guid"); sc_log(ctx, "new key GUID: '%s'", object->guid); } /* Set up the PuKDF info. The public key will be filled in * by the card driver's generate_key function called below. * Auth.ID of the public key object is left empty. */ memset(&pubkey_args, 0, sizeof(pubkey_args)); pubkey_args.id = keygen_args->prkey_args.id; pubkey_args.label = keygen_args->pubkey_label ? keygen_args->pubkey_label : object->label; pubkey_args.usage = keygen_args->prkey_args.usage; pubkey_args.x509_usage = keygen_args->prkey_args.x509_usage; pubkey_args.params.gost = keygen_args->prkey_args.params.gost; /* Generate the private key on card */ r = profile->ops->create_key(profile, p15card, object); LOG_TEST_RET(ctx, r, "Cannot generate key: create key failed"); r = profile->ops->generate_key(profile, p15card, object, &pubkey_args.key); LOG_TEST_RET(ctx, r, "Failed to generate key"); /* update PrKDF entry */ if (!caller_supplied_id) { struct sc_pkcs15_id iid; /* Caller not supplied ID, so, * if intrinsic ID can be calculated -- overwrite the native one */ memset(&iid, 0, sizeof(iid)); r = select_intrinsic_id(p15card, profile, SC_PKCS15_TYPE_PUBKEY, &iid, &pubkey_args.key); LOG_TEST_RET(ctx, r, "Select intrinsic ID error"); if (iid.len) key_info->id = iid; } pubkey_args.id = key_info->id; r = sc_pkcs15_encode_pubkey(ctx, &pubkey_args.key, &object->content.value, &object->content.len); LOG_TEST_RET(ctx, r, "Failed to encode public key"); r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_PRKDF, object); LOG_TEST_RET(ctx, r, "Failed to add generated private key object"); if (!r && profile->ops->emu_store_data) { r = profile->ops->emu_store_data(p15card, profile, object, NULL, NULL); if (r == SC_ERROR_NOT_IMPLEMENTED) r = SC_SUCCESS; LOG_TEST_RET(ctx, r, "Card specific 'store data' failed"); } r = sc_pkcs15init_store_public_key(p15card, profile, &pubkey_args, NULL); LOG_TEST_RET(ctx, r, "Failed to store public key"); if (res_obj) *res_obj = object; sc_pkcs15_erase_pubkey(&pubkey_args.key); profile->dirty = 1; LOG_FUNC_RETURN(ctx, r); } /* * Store private key */ int sc_pkcs15init_store_private_key(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_prkeyargs *keyargs, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *object; struct sc_pkcs15_prkey_info *key_info; struct sc_pkcs15_prkey key; int keybits, idx, r = 0; LOG_FUNC_CALLED(ctx); /* Create a copy of the key first */ key = keyargs->key; r = prkey_fixup(p15card, &key); LOG_TEST_RET(ctx, r, "Private key data sanity check failed"); keybits = prkey_bits(p15card, &key); LOG_TEST_RET(ctx, keybits, "Invalid private key size"); /* Now check whether the card is able to handle this key */ if (check_key_compatibility(p15card, &key, keyargs->x509_usage, keybits, 0)) { /* Make sure the caller explicitly tells us to store * the key as extractable. */ if (!(keyargs->access_flags & SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE)) LOG_TEST_RET(ctx, SC_ERROR_INCOMPATIBLE_KEY, "Card does not support this key."); } /* Select a intrinsic Key ID if user didn't specify one */ r = select_intrinsic_id(p15card, profile, SC_PKCS15_TYPE_PRKEY, &keyargs->id, &keyargs->key); LOG_TEST_RET(ctx, r, "Get intrinsic ID error"); /* Make sure that private key's ID is the unique inside the PKCS#15 application */ r = sc_pkcs15_find_prkey_by_id(p15card, &keyargs->id, NULL); if (!r) LOG_TEST_RET(ctx, SC_ERROR_NON_UNIQUE_ID, "Non unique ID of the private key object"); else if (r != SC_ERROR_OBJECT_NOT_FOUND) LOG_TEST_RET(ctx, r, "Find private key error"); /* Set up the PrKDF object */ r = sc_pkcs15init_init_prkdf(p15card, profile, keyargs, &key, keybits, &object); LOG_TEST_RET(ctx, r, "Failed to initialize private key object"); key_info = (struct sc_pkcs15_prkey_info *) object->data; r = sc_pkcs15init_encode_prvkey_content(p15card, &key, object); LOG_TEST_RET(ctx, r, "Failed to encode public key"); /* Get the number of private keys already on this card */ idx = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, NULL, 0); r = profile->ops->create_key(profile, p15card, object); LOG_TEST_RET(ctx, r, "Card specific 'create key' failed"); r = profile->ops->store_key(profile, p15card, object, &key); LOG_TEST_RET(ctx, r, "Card specific 'store key' failed"); sc_pkcs15_free_object_content(object); r = sc_pkcs15init_encode_prvkey_content(p15card, &key, object); LOG_TEST_RET(ctx, r, "Failed to encode public key"); /* Now update the PrKDF */ r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_PRKDF, object); LOG_TEST_RET(ctx, r, "Failed to add new private key PKCS#15 object"); if (keyargs->guid) { object->guid = strdup(keyargs->guid); if (!object->guid) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate guid"); sc_log(ctx, "new key GUID: '%s'", object->guid); } if (!r && profile->ops->emu_store_data) { r = profile->ops->emu_store_data(p15card, profile, object, NULL, NULL); if (r == SC_ERROR_NOT_IMPLEMENTED) r = SC_SUCCESS; LOG_TEST_RET(ctx, r, "Card specific 'store data' failed"); } if (r >= 0 && res_obj) *res_obj = object; profile->dirty = 1; LOG_FUNC_RETURN(ctx, r); } /* * Store a public key */ int sc_pkcs15init_store_public_key(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_pubkeyargs *keyargs, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *object; struct sc_pkcs15_pubkey_info *key_info; struct sc_pkcs15_keyinfo_gostparams *keyinfo_gostparams; struct sc_pkcs15_pubkey key; struct sc_path *path; const char *label; unsigned int keybits, type, usage; int r; LOG_FUNC_CALLED(ctx); if (!keyargs) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Store public key aborted"); /* Create a copy of the key first */ key = keyargs->key; switch (key.algorithm) { case SC_ALGORITHM_RSA: keybits = sc_pkcs15init_keybits(&key.u.rsa.modulus); type = SC_PKCS15_TYPE_PUBKEY_RSA; break; #ifdef SC_PKCS15_TYPE_PUBKEY_DSA case SC_ALGORITHM_DSA: keybits = sc_pkcs15init_keybits(&key.u.dsa.q); type = SC_PKCS15_TYPE_PUBKEY_DSA; break; #endif case SC_ALGORITHM_GOSTR3410: keybits = SC_PKCS15_GOSTR3410_KEYSIZE; type = SC_PKCS15_TYPE_PUBKEY_GOSTR3410; break; case SC_ALGORITHM_EC: keybits = key.u.ec.params.field_length; type = SC_PKCS15_TYPE_PUBKEY_EC; break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported key algorithm."); } if ((usage = keyargs->usage) == 0) { usage = SC_PKCS15_PRKEY_USAGE_VERIFY; if (keyargs->x509_usage) usage = sc_pkcs15init_map_usage(keyargs->x509_usage, 0); } label = keyargs->label; if (!label) label = "Public Key"; /* Set up the pkcs15 object. */ object = sc_pkcs15init_new_object(type, label, &keyargs->auth_id, NULL); if (object == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate new public key object"); key_info = (struct sc_pkcs15_pubkey_info *) object->data; key_info->usage = usage; key_info->modulus_length = keybits; if (key.algorithm == SC_ALGORITHM_GOSTR3410) { key_info->params.len = sizeof(*keyinfo_gostparams); /* FIXME: malloc() call in pkcs15init, but free() call * in libopensc (sc_pkcs15_free_prkey_info) */ key_info->params.data = malloc(key_info->params.len); if (!key_info->params.data) return SC_ERROR_OUT_OF_MEMORY; keyinfo_gostparams = key_info->params.data; keyinfo_gostparams->gostr3410 = keyargs->params.gost.gostr3410; keyinfo_gostparams->gostr3411 = keyargs->params.gost.gostr3411; keyinfo_gostparams->gost28147 = keyargs->params.gost.gost28147; } else if(key.algorithm == SC_ALGORITHM_EC) key_info->field_length = keybits; /* Select a intrinsic Key ID if the user didn't specify one */ r = select_intrinsic_id(p15card, profile, SC_PKCS15_TYPE_PUBKEY, &keyargs->id, &key); LOG_TEST_RET(ctx, r, "Get intrinsic ID error"); /* Select a Key ID if the user didn't specify one and there is no intrinsic ID, * otherwise make sure it's unique */ r = select_id(p15card, SC_PKCS15_TYPE_PUBKEY, &keyargs->id); LOG_TEST_RET(ctx, r, "Failed to select public key object ID"); /* Make sure that private key's ID is the unique inside the PKCS#15 application */ r = sc_pkcs15_find_pubkey_by_id(p15card, &keyargs->id, NULL); if (!r) LOG_TEST_RET(ctx, SC_ERROR_NON_UNIQUE_ID, "Non unique ID of the public key object"); else if (r != SC_ERROR_OBJECT_NOT_FOUND) LOG_TEST_RET(ctx, r, "Find public key error"); key_info->id = keyargs->id; /* DER encode public key components */ r = sc_pkcs15_encode_pubkey(p15card->card->ctx, &key, &object->content.value, &object->content.len); LOG_TEST_RET(ctx, r, "Encode public key error"); /* Now create key file and store key */ r = sc_pkcs15init_store_data(p15card, profile, object, &object->content, &key_info->path); path = &key_info->path; if (path->count == 0) { path->index = 0; path->count = -1; } /* Update the PuKDF */ if (r >= 0) r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_PUKDF, object); if (r >= 0 && res_obj) *res_obj = object; profile->dirty = 1; LOG_FUNC_RETURN(ctx, r); } /* * Store a certificate */ int sc_pkcs15init_store_certificate(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_certargs *args, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_cert_info *cert_info = NULL; struct sc_pkcs15_object *object = NULL; struct sc_pkcs15_object *key_object = NULL; const char *label = NULL; int r; LOG_FUNC_CALLED(ctx); label = args->label; if (!label) label = "Certificate"; r = select_intrinsic_id(p15card, profile, SC_PKCS15_TYPE_CERT_X509, &args->id, &args->der_encoded); LOG_TEST_RET(ctx, r, "Get certificate 'intrinsic ID' error"); /* Select an ID if the user didn't specify one, otherwise * make sure it's unique */ r = select_id(p15card, SC_PKCS15_TYPE_CERT, &args->id); LOG_TEST_RET(ctx, r, "Select certificate ID error"); object = sc_pkcs15init_new_object(SC_PKCS15_TYPE_CERT_X509, label, NULL, NULL); if (object == NULL) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Failed to allocate certificate object"); cert_info = (struct sc_pkcs15_cert_info *) object->data; cert_info->id = args->id; cert_info->authority = args->authority; sc_der_copy(&object->content, &args->der_encoded); sc_log(ctx, "Store cert(%s,ID:%s,der(%p,%i))", object->label, sc_pkcs15_print_id(&cert_info->id), args->der_encoded.value, args->der_encoded.len); if (profile->pkcs15.direct_certificates) sc_der_copy(&cert_info->value, &args->der_encoded); else r = sc_pkcs15init_store_data(p15card, profile, object, &args->der_encoded, &cert_info->path); /* Now update the CDF */ if (r >= 0) { r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_CDF, object); /* TODO: update private key PKCS#15 object with the certificate's attributes */ } if (r >= 0) { r = sc_pkcs15_prkey_attrs_from_cert(p15card, object, &key_object); if (r) { r = 0; } else if (key_object) { if (profile->ops->emu_update_any_df) { r = profile->ops->emu_update_any_df(profile, p15card, SC_AC_OP_UPDATE, key_object); if (r == SC_ERROR_NOT_SUPPORTED) r = SC_SUCCESS; } else { r = sc_pkcs15init_update_any_df(p15card, profile, key_object->df, 0); sc_log(ctx, "update_any_df returned %i", r); } } } if (r < 0) { sc_pkcs15_remove_object(p15card, object); sc_pkcs15_free_object(object); } else if (res_obj) { *res_obj = object; } profile->dirty = 1; LOG_FUNC_RETURN(ctx, r); } /* * Store a data object */ int sc_pkcs15init_store_data_object(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15init_dataargs *args, struct sc_pkcs15_object **res_obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_data_info *data_object_info; struct sc_pkcs15_object *object; struct sc_pkcs15_object *objs[32]; const char *label; int r, i; unsigned int tid = 0x01; LOG_FUNC_CALLED(ctx); label = args->label; if (!args->id.len) { /* Select an ID if the user didn't specify one, otherwise * make sure it's unique (even though data objects doesn't * have a pkcs15 id we need one here to create a unique * file id from the data file template */ r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_DATA_OBJECT, objs, 32); LOG_TEST_RET(ctx, r, "Get 'DATA' objects error"); for (i = 0; i < r; i++) { unsigned char cid; struct sc_pkcs15_data_info *cinfo = (struct sc_pkcs15_data_info *) objs[i]->data; if (!cinfo->path.len) continue; cid = cinfo->path.value[cinfo->path.len - 1]; if (cid >= tid) tid = cid + 1; } if (tid > 0xff) /* too many data objects ... */ return SC_ERROR_TOO_MANY_OBJECTS; args->id.len = 1; args->id.value[0] = tid; } else { /* in case the user specifies an id it should be at most * one byte long */ if (args->id.len > 1) return SC_ERROR_INVALID_ARGUMENTS; } object = sc_pkcs15init_new_object(SC_PKCS15_TYPE_DATA_OBJECT, label, &args->auth_id, NULL); if (object == NULL) return SC_ERROR_OUT_OF_MEMORY; data_object_info = (struct sc_pkcs15_data_info *) object->data; if (args->app_label != NULL) strlcpy(data_object_info->app_label, args->app_label, sizeof(data_object_info->app_label)); else if (label != NULL) strlcpy(data_object_info->app_label, label, sizeof(data_object_info->app_label)); data_object_info->app_oid = args->app_oid; sc_der_copy(&data_object_info->data, &args->der_encoded); r = sc_pkcs15init_store_data(p15card, profile, object, &args->der_encoded, &data_object_info->path); LOG_TEST_RET(ctx, r, "Store 'DATA' object error"); /* Now update the DDF */ r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_DODF, object); LOG_TEST_RET(ctx, r, "'DODF' update error"); if (r >= 0 && res_obj) *res_obj = object; profile->dirty = 1; LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15init_get_pin_reference(struct sc_pkcs15_card *p15card, struct sc_profile *profile, unsigned auth_method, int reference) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_auth_info auth_info; struct sc_pkcs15_object *auth_objs[0x10]; int r, ii, nn_objs; LOG_FUNC_CALLED(ctx); /* 1. Look for the corresponding pkcs15 PIN object. */ /* Get all existing pkcs15 AUTH objects */ r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, auth_objs, 0x10); LOG_TEST_RET(ctx, r, "Get PKCS#15 AUTH objects error"); nn_objs = r; sc_log(ctx, "found %i auth objects; looking for AUTH object(auth_method:%i,reference:%i)", nn_objs, auth_method, reference); for (ii=0; iidata; struct sc_pkcs15_pin_attributes *pin_attrs = &auth_info->attrs.pin; sc_log(ctx, "check PIN(%s,auth_method:%i,type:%i,reference:%i,flags:%X)", auth_objs[ii]->label, auth_info->auth_method, pin_attrs->type, pin_attrs->reference, pin_attrs->flags); /* Find out if there is AUTH pkcs15 object with given 'type' and 'reference' */ if (auth_info->auth_method == auth_method && pin_attrs->reference == reference) LOG_FUNC_RETURN(ctx, pin_attrs->reference); if (auth_method != SC_AC_SYMBOLIC) continue; /* Translate 'SYMBOLIC' PIN reference into the pkcs#15 pinAttributes.flags * and check for the existing pkcs15 PIN object with these flags. */ switch (reference) { case SC_PKCS15INIT_USER_PIN: if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) continue; if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) continue; break; case SC_PKCS15INIT_SO_PIN: if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN) continue; if (!(pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN)) continue; break; case SC_PKCS15INIT_USER_PUK: if (pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN) continue; if (!(pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) continue; break; case SC_PKCS15INIT_SO_PUK: if (!(pin_attrs->flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) continue; if (!(pin_attrs->flags & SC_PKCS15_PIN_FLAG_SO_PIN)) continue; break; default: LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid Symbolic PIN reference"); } LOG_FUNC_RETURN(ctx, pin_attrs->reference); } /* 2. No existing pkcs15 PIN object * -- check if profile defines some PIN with 'reference' as PIN reference. */ r = sc_profile_get_pin_id_by_reference(profile, auth_method, reference, &auth_info); if (r < 0) LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "PIN template not found"); LOG_FUNC_RETURN(ctx, auth_info.attrs.pin.reference); } static int sc_pkcs15init_store_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, struct sc_pkcs15_der *data, struct sc_path *path) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; int r; LOG_FUNC_CALLED(ctx); if (profile->ops->emu_store_data) { r = profile->ops->emu_store_data(p15card, profile, object, data, path); if (r == SC_SUCCESS || r != SC_ERROR_NOT_IMPLEMENTED) LOG_FUNC_RETURN(ctx, r); } r = select_object_path(p15card, profile, object, path); LOG_TEST_RET(ctx, r, "Failed to select object path"); r = sc_profile_get_file_by_path(profile, path, &file); LOG_TEST_RET(ctx, r, "Failed to get file by path"); if (file->path.count == 0) { file->path.index = 0; file->path.count = -1; } r = sc_pkcs15init_delete_by_path(profile, p15card, &file->path); if (r && r != SC_ERROR_FILE_NOT_FOUND) LOG_TEST_RET(ctx, r, "Cannot delete file"); r = sc_pkcs15init_update_file(profile, p15card, file, data->value, data->len); *path = file->path; if (file) sc_file_free(file); LOG_FUNC_RETURN(ctx, r); } /* * Map X509 keyUsage extension bits to PKCS#15 keyUsage bits */ typedef struct { unsigned long x509_usage; unsigned int p15_usage; } sc_usage_map; static sc_usage_map x509_to_pkcs15_private_key_usage[16] = { { SC_PKCS15INIT_X509_DIGITAL_SIGNATURE, SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER }, { SC_PKCS15INIT_X509_NON_REPUDIATION, SC_PKCS15_PRKEY_USAGE_NONREPUDIATION }, { SC_PKCS15INIT_X509_KEY_ENCIPHERMENT, SC_PKCS15_PRKEY_USAGE_UNWRAP }, { SC_PKCS15INIT_X509_DATA_ENCIPHERMENT, SC_PKCS15_PRKEY_USAGE_DECRYPT }, { SC_PKCS15INIT_X509_KEY_AGREEMENT, SC_PKCS15_PRKEY_USAGE_DERIVE }, { SC_PKCS15INIT_X509_KEY_CERT_SIGN, SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER }, { SC_PKCS15INIT_X509_CRL_SIGN, SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER } }; static sc_usage_map x509_to_pkcs15_public_key_usage[16] = { { SC_PKCS15INIT_X509_DIGITAL_SIGNATURE, SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER }, { SC_PKCS15INIT_X509_NON_REPUDIATION, SC_PKCS15_PRKEY_USAGE_NONREPUDIATION }, { SC_PKCS15INIT_X509_KEY_ENCIPHERMENT, SC_PKCS15_PRKEY_USAGE_WRAP }, { SC_PKCS15INIT_X509_DATA_ENCIPHERMENT, SC_PKCS15_PRKEY_USAGE_ENCRYPT }, { SC_PKCS15INIT_X509_KEY_AGREEMENT, SC_PKCS15_PRKEY_USAGE_DERIVE }, { SC_PKCS15INIT_X509_KEY_CERT_SIGN, SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER }, { SC_PKCS15INIT_X509_CRL_SIGN, SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER } }; static int sc_pkcs15init_map_usage(unsigned long x509_usage, int _private) { unsigned int p15_usage = 0, n; sc_usage_map *map; map = _private ? x509_to_pkcs15_private_key_usage : x509_to_pkcs15_public_key_usage; for (n = 0; n < 16; n++) { if (x509_usage & map[n].x509_usage) p15_usage |= map[n].p15_usage; } return p15_usage; } /* * Compute modulus length */ static size_t sc_pkcs15init_keybits(struct sc_pkcs15_bignum *bn) { unsigned int mask, bits; if (!bn || !bn->len) return 0; bits = bn->len << 3; for (mask = 0x80; mask && !(bn->data[0] & mask); mask >>= 1) bits--; return bits; } /* * Check consistency of the key parameters. */ static int check_keygen_params_consistency(struct sc_card *card, struct sc_pkcs15init_keygen_args *params, unsigned int keybits, unsigned int *out_keybits) { struct sc_context *ctx = card->ctx; unsigned int alg = params->prkey_args.key.algorithm; int i, rv; if (alg == SC_ALGORITHM_EC) { struct sc_pkcs15_ec_parameters *ecparams = ¶ms->prkey_args.params.ec; rv = sc_pkcs15_fix_ec_parameters(ctx, ecparams); LOG_TEST_RET(ctx, rv, "Cannot fix EC parameters"); sc_log(ctx, "EC parameters: %s", sc_dump_hex(ecparams->der.value, ecparams->der.len)); if (!keybits) keybits = ecparams->field_length; } if (out_keybits) *out_keybits = keybits; for (i = 0; i < card->algorithm_count; i++) { struct sc_algorithm_info *info = &card->algorithms[i]; if (info->algorithm != alg) continue; if (info->key_length != keybits) continue; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); } /* * Check whether the card has native crypto support for this key. */ static int check_key_compatibility(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey *key, unsigned int x509_usage, unsigned int key_length, unsigned int flags) { struct sc_algorithm_info *info; unsigned int count; count = p15card->card->algorithm_count; for (info = p15card->card->algorithms; count--; info++) { /* don't check flags if none was specified */ if (info->algorithm != key->algorithm || info->key_length != key_length) continue; if (flags != 0 && ((info->flags & flags) != flags)) continue; if (key->algorithm == SC_ALGORITHM_RSA) { if (info->u._rsa.exponent != 0 && key->u.rsa.exponent.len != 0) { struct sc_pkcs15_bignum *e = &key->u.rsa.exponent; unsigned long exponent = 0; unsigned int n; if (e->len > 4) continue; for (n = 0; n < e->len; n++) { exponent <<= 8; exponent |= e->data[n]; } if (info->u._rsa.exponent != exponent) continue; } } else if (key->algorithm == SC_ALGORITHM_EC) { } return SC_SUCCESS; } return SC_ERROR_OBJECT_NOT_VALID; } /* * Check RSA key for consistency, and compute missing * CRT elements */ static int prkey_fixup_rsa(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey_rsa *key) { struct sc_context *ctx = p15card->card->ctx; if (!key->modulus.len || !key->exponent.len || !key->d.len || !key->p.len || !key->q.len) { sc_log(ctx, "Missing private RSA coefficient"); return SC_ERROR_INVALID_ARGUMENTS; } #ifdef ENABLE_OPENSSL #define GETBN(dst, src, mem) \ do { dst.len = BN_num_bytes(src); \ assert(dst.len <= sizeof(mem)); \ BN_bn2bin(src, dst.data = mem); \ } while (0) /* Generate additional parameters. * At least the GPK seems to need the full set of CRT * parameters; storing just the private exponent produces * invalid signatures. * The cryptoflex does not seem to be able to do any sort * of RSA without the full set of CRT coefficients either */ if (!key->dmp1.len || !key->dmq1.len || !key->iqmp.len) { static u8 dmp1[256], dmq1[256], iqmp[256]; RSA *rsa; BIGNUM *aux; BN_CTX *bn_ctx; rsa = RSA_new(); rsa->n = BN_bin2bn(key->modulus.data, key->modulus.len, NULL); rsa->e = BN_bin2bn(key->exponent.data, key->exponent.len, NULL); rsa->d = BN_bin2bn(key->d.data, key->d.len, NULL); rsa->p = BN_bin2bn(key->p.data, key->p.len, NULL); rsa->q = BN_bin2bn(key->q.data, key->q.len, NULL); if (!rsa->dmp1) rsa->dmp1 = BN_new(); if (!rsa->dmq1) rsa->dmq1 = BN_new(); if (!rsa->iqmp) rsa->iqmp = BN_new(); aux = BN_new(); bn_ctx = BN_CTX_new(); BN_sub(aux, rsa->q, BN_value_one()); BN_mod(rsa->dmq1, rsa->d, aux, bn_ctx); BN_sub(aux, rsa->p, BN_value_one()); BN_mod(rsa->dmp1, rsa->d, aux, bn_ctx); BN_mod_inverse(rsa->iqmp, rsa->q, rsa->p, bn_ctx); BN_clear_free(aux); BN_CTX_free(bn_ctx); /* Not thread safe, but much better than a memory leak */ GETBN(key->dmp1, rsa->dmp1, dmp1); GETBN(key->dmq1, rsa->dmq1, dmq1); GETBN(key->iqmp, rsa->iqmp, iqmp); RSA_free(rsa); } #undef GETBN #endif return 0; } static int prkey_fixup(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey *key) { switch (key->algorithm) { case SC_ALGORITHM_RSA: return prkey_fixup_rsa(p15card, &key->u.rsa); case SC_ALGORITHM_DSA: case SC_ALGORITHM_GOSTR3410: /* for now */ return 0; } return 0; } static int prkey_bits(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey *key) { struct sc_context *ctx = p15card->card->ctx; switch (key->algorithm) { case SC_ALGORITHM_RSA: return sc_pkcs15init_keybits(&key->u.rsa.modulus); case SC_ALGORITHM_DSA: return sc_pkcs15init_keybits(&key->u.dsa.q); case SC_ALGORITHM_GOSTR3410: if (sc_pkcs15init_keybits(&key->u.gostr3410.d) > SC_PKCS15_GOSTR3410_KEYSIZE) { sc_log(ctx, "Unsupported key (keybits %u)", sc_pkcs15init_keybits(&key->u.gostr3410.d)); return SC_ERROR_OBJECT_NOT_VALID; } return SC_PKCS15_GOSTR3410_KEYSIZE; case SC_ALGORITHM_EC: /* calculation returns one bit too small, add one bu default */ sc_log(ctx, "Private EC key length %u", sc_pkcs15init_keybits(&key->u.ec.privateD) + 1); return sc_pkcs15init_keybits(&key->u.ec.privateD) + 1; } sc_log(ctx, "Unsupported key algorithm."); return SC_ERROR_NOT_SUPPORTED; } static int prkey_pkcs15_algo(struct sc_pkcs15_card *p15card, struct sc_pkcs15_prkey *key) { struct sc_context *ctx = p15card->card->ctx; switch (key->algorithm) { case SC_ALGORITHM_RSA: return SC_PKCS15_TYPE_PRKEY_RSA; case SC_ALGORITHM_DSA: return SC_PKCS15_TYPE_PRKEY_DSA; case SC_ALGORITHM_GOSTR3410: return SC_PKCS15_TYPE_PRKEY_GOSTR3410; case SC_ALGORITHM_EC: return SC_PKCS15_TYPE_PRKEY_EC; } sc_log(ctx, "Unsupported key algorithm."); return SC_ERROR_NOT_SUPPORTED; } static struct sc_pkcs15_df * find_df_by_type(struct sc_pkcs15_card *p15card, unsigned int type) { struct sc_pkcs15_df *df = p15card->df_list; while (df != NULL && df->type != type) df = df->next; return df; } static int select_intrinsic_id(struct sc_pkcs15_card *p15card, struct sc_profile *profile, int type, struct sc_pkcs15_id *id, void *data) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_pubkey *pubkey = NULL; unsigned id_style = profile->id_style; int rv, allocated = 0; LOG_FUNC_CALLED(ctx); #ifndef ENABLE_OPENSSL LOG_FUNC_RETURN(ctx, SC_SUCCESS); #else /* ID already exists */ if (id->len) LOG_FUNC_RETURN(ctx, SC_SUCCESS); /* Native ID style is not an intrisic one */ if (profile->id_style == SC_PKCS15INIT_ID_STYLE_NATIVE) LOG_FUNC_RETURN(ctx, SC_SUCCESS); /* Get PKCS15 public key */ switch(type) { case SC_PKCS15_TYPE_CERT_X509: rv = sc_pkcs15_pubkey_from_cert(ctx, (struct sc_pkcs15_der *)data, &pubkey); LOG_TEST_RET(ctx, rv, "X509 parse error"); allocated = 1; break; case SC_PKCS15_TYPE_PRKEY: rv = sc_pkcs15_pubkey_from_prvkey(ctx, (struct sc_pkcs15_prkey *)data, &pubkey); LOG_TEST_RET(ctx, rv, "Cannot get public key"); allocated = 1; break; case SC_PKCS15_TYPE_PUBKEY: pubkey = (struct sc_pkcs15_pubkey *)data; allocated = 0; break; default: sc_log(ctx, "Intrinsic ID is not implemented for the object type 0x%X", type); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* Skip silently if key is not inintialized. */ if (pubkey->algorithm == SC_ALGORITHM_RSA && !pubkey->u.rsa.modulus.len) goto done; else if (pubkey->algorithm == SC_ALGORITHM_DSA && !pubkey->u.dsa.pub.data) goto done; else if (pubkey->algorithm == SC_ALGORITHM_GOSTR3410 && !pubkey->u.gostr3410.xy.data) goto done; else if (pubkey->algorithm == SC_ALGORITHM_EC && !pubkey->u.ec.ecpointQ.value) goto done; /* In Mozilla 'GOST R 34.10' is not yet supported. * So, switch to the ID recommended by RFC2459 */ if (pubkey->algorithm == SC_ALGORITHM_GOSTR3410 && id_style == SC_PKCS15INIT_ID_STYLE_MOZILLA) id_style = SC_PKCS15INIT_ID_STYLE_RFC2459; if (id_style == SC_PKCS15INIT_ID_STYLE_MOZILLA) { if (pubkey->algorithm == SC_ALGORITHM_RSA) SHA1(pubkey->u.rsa.modulus.data, pubkey->u.rsa.modulus.len, id->value); else if (pubkey->algorithm == SC_ALGORITHM_DSA) SHA1(pubkey->u.dsa.pub.data, pubkey->u.dsa.pub.len, id->value); else if (pubkey->algorithm == SC_ALGORITHM_EC) { /* ID should be SHA1 of the X coordinate according to PKCS#15 v1.1 */ /* skip the 04 tag and get the X component */ SHA1(pubkey->u.ec.ecpointQ.value+1, (pubkey->u.ec.ecpointQ.len - 1) / 2, id->value); } else goto done; id->len = SHA_DIGEST_LENGTH; } else if (id_style == SC_PKCS15INIT_ID_STYLE_RFC2459) { unsigned char *id_data = NULL; size_t id_data_len = 0; rv = sc_pkcs15_encode_pubkey(ctx, pubkey, &id_data, &id_data_len); LOG_TEST_RET(ctx, rv, "Encoding public key error"); if (!id_data || !id_data_len) LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "Encoding public key error"); SHA1(id_data, id_data_len, id->value); id->len = SHA_DIGEST_LENGTH; free(id_data); } else { sc_log(ctx, "Unsupported ID style: %i", profile->id_style); LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Non supported ID style"); } done: if (allocated) sc_pkcs15_free_pubkey(pubkey); LOG_FUNC_RETURN(ctx, id->len); #endif } static int select_id(struct sc_pkcs15_card *p15card, int type, struct sc_pkcs15_id *id) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_id unused_id; struct sc_pkcs15_object *obj; unsigned int nid = DEFAULT_ID; int r; LOG_FUNC_CALLED(ctx); /* If the user provided an ID, make sure we can use it */ if (id->len != 0) { r = sc_pkcs15_find_object_by_id(p15card, type, id, &obj); if (r == SC_ERROR_OBJECT_NOT_FOUND) r = 0; LOG_FUNC_RETURN(ctx, r); } memset(&unused_id, 0, sizeof(unused_id)); while (nid < 255) { id->value[0] = nid++; id->len = 1; r = sc_pkcs15_find_object_by_id(p15card, type, id, &obj); if (r == SC_ERROR_OBJECT_NOT_FOUND) { /* We don't have an object of that type yet. * If we're allocating a PRKEY object, make * sure there's no conflicting pubkey or cert * object either. */ if (type == SC_PKCS15_TYPE_PRKEY) { struct sc_pkcs15_search_key search_key; memset(&search_key, 0, sizeof(search_key)); search_key.class_mask = SC_PKCS15_SEARCH_CLASS_PUBKEY | SC_PKCS15_SEARCH_CLASS_CERT; search_key.id = id; r = sc_pkcs15_search_objects(p15card, &search_key, NULL, 0); /* If there is a pubkey or cert with * this ID, skip it. */ if (r > 0) continue; } if (!unused_id.len) unused_id = *id; continue; } } if (unused_id.len) { *id = unused_id; LOG_FUNC_RETURN(ctx, 0); } LOG_FUNC_RETURN(ctx, SC_ERROR_TOO_MANY_OBJECTS); } /* * Select a path for a new object * 1. If the object is to be protected by a PIN, use the path * given in the PIN auth object * 2. Otherwise, use the path of the application DF * 3. If the profile defines a key-dir template, the new object * should go into a subdirectory of the selected DF: * Instantiate the template, using the ID of the new object * to uniquify the path. Inside the instantiated template, * look for a file corresponding to the type of object we * wish to create ("private-key", "public-key" etc). */ static const char * get_template_name_from_object (struct sc_pkcs15_object *obj) { switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: return "private-key"; case SC_PKCS15_TYPE_PUBKEY: return "public-key"; case SC_PKCS15_TYPE_CERT: return "certificate"; case SC_PKCS15_TYPE_DATA_OBJECT: if (obj->flags & SC_PKCS15_CO_FLAG_PRIVATE) return "privdata"; else return "data"; } return NULL; } static int get_object_path_from_object (struct sc_pkcs15_object *obj, struct sc_path *ret_path) { if (!ret_path) return SC_ERROR_INVALID_ARGUMENTS; memset(ret_path, 0, sizeof(struct sc_path)); switch(obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PRKEY: *ret_path = ((struct sc_pkcs15_prkey_info *)obj->data)->path; return SC_SUCCESS; case SC_PKCS15_TYPE_PUBKEY: *ret_path = ((struct sc_pkcs15_pubkey_info *)obj->data)->path; return SC_SUCCESS; case SC_PKCS15_TYPE_CERT: *ret_path = ((struct sc_pkcs15_cert_info *)obj->data)->path; return SC_SUCCESS; case SC_PKCS15_TYPE_DATA_OBJECT: *ret_path = ((struct sc_pkcs15_data_info *)obj->data)->path; return SC_SUCCESS; case SC_PKCS15_TYPE_AUTH: *ret_path = ((struct sc_pkcs15_auth_info *)obj->data)->path; return SC_SUCCESS; } return SC_ERROR_NOT_SUPPORTED; } static int select_object_path(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj, struct sc_path *path) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file; struct sc_pkcs15_object *objs[32]; struct sc_pkcs15_id indx_id; struct sc_path obj_path; int ii, r, nn_objs, indx; const char *name; LOG_FUNC_CALLED(ctx); r = sc_pkcs15_get_objects(p15card, obj->type & SC_PKCS15_TYPE_CLASS_MASK, objs, sizeof(objs)/sizeof(objs[0])); LOG_TEST_RET(ctx, r, "Get PKCS#15 objects error"); nn_objs = r; /* For cards with a pin-domain profile, we need * to put the key below the DF of the specified PIN */ memset(path, 0, sizeof(*path)); if (obj->auth_id.len && profile->pin_domains != 0) { r = sc_pkcs15init_get_pin_path(p15card, &obj->auth_id, path); LOG_TEST_RET(ctx, r, "Cannot get PIN path"); } else { *path = profile->df_info->file->path; } /* If the profile specifies a key directory template, * instantiate it now and create the DF */ name = get_template_name_from_object (obj); if (!name) LOG_FUNC_RETURN(ctx, SC_SUCCESS); sc_log(ctx, "key-domain.%s @%s (auth_id.len=%d)", name, sc_print_path(path), obj->auth_id.len); indx_id.len = 1; for (indx = TEMPLATE_INSTANTIATE_MIN_INDEX; indx <= TEMPLATE_INSTANTIATE_MAX_INDEX; indx++) { indx_id.value[0] = indx; r = sc_profile_instantiate_template(profile, "key-domain", path, name, &indx_id, &file); if (r == SC_ERROR_TEMPLATE_NOT_FOUND) { /* No template in 'key-domain' -- try to instantiate the template-'object name' * outside of the 'key-domain' scope. */ char t_name[0x40]; snprintf(t_name, sizeof(t_name), "template-%s", name); sc_log(ctx, "get instance %i of '%s'", indx, t_name); r = sc_profile_get_file_instance(profile, t_name, indx, &file); if (r == SC_ERROR_FILE_NOT_FOUND) LOG_FUNC_RETURN(ctx, SC_SUCCESS); } LOG_TEST_RET(ctx, r, "Template instantiation error"); if (file->type == SC_FILE_TYPE_BSO) break; sc_log(ctx, "instantiated template path %s", sc_print_path(&file->path)); for (ii=0; iipath.len) break; if (!memcmp(obj_path.value, file->path.value, obj_path.len)) break; } if (ii==nn_objs) break; if (obj_path.len != file->path.len) break; sc_file_free(file); indx_id.value[0] += 1; } if (indx > TEMPLATE_INSTANTIATE_MAX_INDEX) LOG_TEST_RET(ctx, SC_ERROR_TOO_MANY_OBJECTS, "Template instantiation error"); *path = file->path; sc_file_free(file); sc_log(ctx, "returns object path '%s'", sc_print_path(path)); LOG_FUNC_RETURN(ctx, SC_SUCCESS); } /* * Update EF(DIR) */ static int sc_pkcs15init_update_dir(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_app_info *app) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; int r, retry = 1; LOG_FUNC_CALLED(ctx); if (profile->ops->emu_update_dir) { r = profile->ops->emu_update_dir(profile, p15card, app); LOG_FUNC_RETURN(ctx, r); } do { struct sc_file *dir_file; struct sc_path path; r = sc_enum_apps(card); if (r != SC_ERROR_FILE_NOT_FOUND) break; /* DIR file is not yet created. */ sc_format_path("3F002F00", &path); r = sc_profile_get_file_by_path(profile, &path, &dir_file); LOG_TEST_RET(ctx, r, "DIR file not defined in profile"); /* Create DIR file */ r = sc_pkcs15init_update_file(profile, p15card, dir_file, NULL, 0); sc_file_free(dir_file); } while (retry--); if (r >= 0) { card->app[card->app_count++] = app; r = sc_update_dir(card, NULL); } LOG_FUNC_RETURN(ctx, r); } static char * get_generalized_time(struct sc_context *ctx) { #ifdef HAVE_GETTIMEOFDAY struct timeval tv; #endif struct tm *tm_time; time_t t; char* ret; size_t r; #ifdef HAVE_GETTIMEOFDAY gettimeofday(&tv, NULL); t = tv.tv_sec; #else t = time(NULL); #endif tm_time = gmtime(&t); if (tm_time == NULL) { sc_log(ctx, "error: gmtime failed"); return NULL; } ret = calloc(1, 16); if (ret == NULL) { sc_log(ctx, "error: calloc failed"); return NULL; } /* print time in generalized time format */ r = strftime(ret, 16, "%Y%m%d%H%M%SZ", tm_time); if (r == 0) { sc_log(ctx, "error: strftime failed"); free(ret); return NULL; } return ret; } static int sc_pkcs15init_update_tokeninfo(struct sc_pkcs15_card *p15card, struct sc_profile *profile) { struct sc_card *card = p15card->card; struct sc_pkcs15_tokeninfo tokeninfo; unsigned char *buf = NULL; size_t size; int r; LOG_FUNC_CALLED(p15card->card->ctx); /* set lastUpdate field */ if (p15card->tokeninfo->last_update.gtime != NULL) free(p15card->tokeninfo->last_update.gtime); p15card->tokeninfo->last_update.gtime = get_generalized_time(card->ctx); if (p15card->tokeninfo->last_update.gtime == NULL) return SC_ERROR_INTERNAL; tokeninfo = *(p15card->tokeninfo); if (profile->ops->emu_update_tokeninfo) return profile->ops->emu_update_tokeninfo(profile, p15card, &tokeninfo); r = sc_pkcs15_encode_tokeninfo(card->ctx, &tokeninfo, &buf, &size); if (r >= 0) r = sc_pkcs15init_update_file(profile, p15card, p15card->file_tokeninfo, buf, size); if (buf) free(buf); LOG_FUNC_RETURN(p15card->card->ctx, r); } static int sc_pkcs15init_update_lastupdate(struct sc_pkcs15_card *p15card, struct sc_profile *profile) { struct sc_context *ctx = p15card->card->ctx; int r; LOG_FUNC_CALLED(ctx); if (p15card->tokeninfo->last_update.path.len) { static const struct sc_asn1_entry c_asn1_last_update[2] = { { "generalizedTime", SC_ASN1_GENERALIZEDTIME, SC_ASN1_TAG_GENERALIZEDTIME, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; struct sc_asn1_entry asn1_last_update[2]; size_t lupdate_len; struct sc_file *file = NULL; struct sc_pkcs15_last_update *last_update = &p15card->tokeninfo->last_update; unsigned char *buf = NULL; size_t buflen; /* update 'lastUpdate' file */ if (last_update->gtime != NULL) free(last_update->gtime); last_update->gtime = get_generalized_time(ctx); if (last_update->gtime == NULL) return SC_ERROR_INTERNAL; sc_copy_asn1_entry(c_asn1_last_update, asn1_last_update); lupdate_len = strlen(last_update->gtime); sc_format_asn1_entry(asn1_last_update + 0, last_update->gtime, &lupdate_len, 1); r = sc_asn1_encode(ctx, asn1_last_update, &buf, &buflen); LOG_TEST_RET(ctx, r, "select object path failed"); r = sc_select_file(p15card->card, &last_update->path, &file); LOG_TEST_RET(ctx, r, "select object path failed"); r = sc_pkcs15init_update_file(profile, p15card, file, buf, buflen); sc_file_free(file); if (buf) free(buf); LOG_TEST_RET(ctx, r, "Cannot update 'LastUpdate' file"); LOG_FUNC_RETURN(ctx, r); } r = sc_pkcs15init_update_tokeninfo(p15card, profile); LOG_FUNC_RETURN(ctx, r); } static int sc_pkcs15init_update_odf(struct sc_pkcs15_card *p15card, struct sc_profile *profile) { struct sc_context *ctx = p15card->card->ctx; unsigned char *buf = NULL; size_t size; int r; LOG_FUNC_CALLED(ctx); r = sc_pkcs15_encode_odf(ctx, p15card, &buf, &size); if (r >= 0) r = sc_pkcs15init_update_file(profile, p15card, p15card->file_odf, buf, size); if (buf) free(buf); LOG_FUNC_RETURN(ctx, r); } /* * Update any PKCS15 DF file (except ODF and DIR) */ int sc_pkcs15init_update_any_df(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_df *df, int is_new) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; struct sc_file *file = NULL; unsigned char *buf = NULL; size_t bufsize; int update_odf = is_new, r = 0; LOG_FUNC_CALLED(ctx); sc_profile_get_file_by_path(profile, &df->path, &file); if (file == NULL) sc_select_file(card, &df->path, &file); r = sc_pkcs15_encode_df(card->ctx, p15card, df, &buf, &bufsize); if (r >= 0) { r = sc_pkcs15init_update_file(profile, p15card, file, buf, bufsize); /* For better performance and robustness, we want * to note which portion of the file actually * contains valid data. * * This is particularly useful if we store certificates * directly in the CDF - we may want to make the CDF * fairly big, without having to read the entire file * every time we parse the CDF. */ if (profile->pkcs15.encode_df_length) { df->path.count = bufsize; df->path.index = 0; update_odf = 1; } free(buf); } if (file) sc_file_free(file); LOG_TEST_RET(ctx, r, "Failed to encode or update xDF"); /* Now update the ODF if we have to */ if (update_odf) r = sc_pkcs15init_update_odf(p15card, profile); LOG_TEST_RET(ctx, r, "Failed to encode or update ODF"); LOG_FUNC_RETURN(ctx, r > 0 ? SC_SUCCESS : r); } /* * Add an object to one of the pkcs15 directory files. */ int sc_pkcs15init_add_object(struct sc_pkcs15_card *p15card, struct sc_profile *profile, unsigned int df_type, struct sc_pkcs15_object *object) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_df *df; int is_new = 0, r = 0, object_added = 0; LOG_FUNC_CALLED(ctx); sc_log(ctx, "add object %p to DF of type %u", object, df_type); df = find_df_by_type(p15card, df_type); if (df == NULL) { struct sc_file *file; file = profile->df[df_type]; if (file == NULL) { sc_log(ctx, "Profile doesn't define a DF file %u", df_type); LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "DF not found in profile"); } sc_pkcs15_add_df(p15card, df_type, &file->path); df = find_df_by_type(p15card, df_type); assert(df != NULL); is_new = 1; /* Mark the df as enumerated, so libopensc doesn't try * to load the file at a most inconvenient moment */ df->enumerated = 1; } if (object == NULL) { sc_log(ctx, "Add nothing; just instantiate this directory file"); } else if (object->df == NULL) { sc_log(ctx, "Append object"); object->df = df; r = sc_pkcs15_add_object(p15card, object); LOG_TEST_RET(ctx, r, "Failed to add pkcs15 object"); object_added = 1; } else { sc_log(ctx, "Reuse existing object"); assert(object->df == df); } if (profile->ops->emu_update_any_df) r = profile->ops->emu_update_any_df(profile, p15card, SC_AC_OP_CREATE, object); else r = sc_pkcs15init_update_any_df(p15card, profile, df, is_new); if (r < 0 && object_added) sc_pkcs15_remove_object(p15card, object); LOG_FUNC_RETURN(ctx, r > 0 ? SC_SUCCESS : r); } struct sc_pkcs15_object * sc_pkcs15init_new_object(int type, const char *label, struct sc_pkcs15_id *auth_id, void *data) { struct sc_pkcs15_object *object; unsigned int data_size = 0; object = calloc(1, sizeof(*object)); if (object == NULL) return NULL; object->type = type; switch (type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_AUTH: object->flags = DEFAULT_PIN_FLAGS; data_size = sizeof(struct sc_pkcs15_auth_info); break; case SC_PKCS15_TYPE_PRKEY: object->flags = DEFAULT_PRKEY_FLAGS; data_size = sizeof(struct sc_pkcs15_prkey_info); break; case SC_PKCS15_TYPE_PUBKEY: object->flags = DEFAULT_PUBKEY_FLAGS; data_size = sizeof(struct sc_pkcs15_pubkey_info); break; case SC_PKCS15_TYPE_CERT: object->flags = DEFAULT_CERT_FLAGS; data_size = sizeof(struct sc_pkcs15_cert_info); break; case SC_PKCS15_TYPE_DATA_OBJECT: object->flags = DEFAULT_DATA_FLAGS; if (auth_id->len != 0) object->flags |= SC_PKCS15_CO_FLAG_PRIVATE; data_size = sizeof(struct sc_pkcs15_data_info); break; } if (data_size) { object->data = calloc(1, data_size); if (data) memcpy(object->data, data, data_size); } if (label) strlcpy(object->label, label, sizeof(object->label)); if (auth_id) object->auth_id = *auth_id; return object; } int sc_pkcs15init_change_attrib(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, int new_attrib_type, void *new_value, int new_len) { struct sc_context *ctx = p15card->card->ctx; struct sc_card *card = p15card->card; unsigned char *buf = NULL; size_t bufsize; int df_type, r = 0; struct sc_pkcs15_df *df; struct sc_pkcs15_id new_id = *((struct sc_pkcs15_id *) new_value); if (object == NULL || object->df == NULL) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Cannot change attribute"); df_type = object->df->type; df = find_df_by_type(p15card, df_type); if (df == NULL) LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "Cannot change attribute"); switch(new_attrib_type) { case P15_ATTR_TYPE_LABEL: if (new_len >= SC_PKCS15_MAX_LABEL_SIZE) LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "New label too long"); memcpy(object->label, new_value, new_len); object->label[new_len] = '\0'; break; case P15_ATTR_TYPE_ID: switch(df_type) { case SC_PKCS15_PRKDF: ((struct sc_pkcs15_prkey_info *) object->data)->id = new_id; break; case SC_PKCS15_PUKDF: case SC_PKCS15_PUKDF_TRUSTED: ((struct sc_pkcs15_pubkey_info *) object->data)->id = new_id; break; case SC_PKCS15_CDF: case SC_PKCS15_CDF_TRUSTED: case SC_PKCS15_CDF_USEFUL: ((struct sc_pkcs15_cert_info *) object->data)->id = new_id; break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Cannot change ID attribute"); } break; default: LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Only 'LABEL' or 'ID' attributes can be changed"); } if (profile->ops->emu_update_any_df) { r = profile->ops->emu_update_any_df(profile, p15card, SC_AC_OP_CREATE, object); LOG_TEST_RET(ctx, r, "Card specific DF update failed"); } else { r = sc_pkcs15_encode_df(card->ctx, p15card, df, &buf, &bufsize); if (r >= 0) { struct sc_file *file = NULL; r = sc_profile_get_file_by_path(profile, &df->path, &file); LOG_TEST_RET(ctx, r, "Cannot instantiate file by path"); r = sc_pkcs15init_update_file(profile, p15card, file, buf, bufsize); free(buf); sc_file_free(file); } } return r < 0 ? r : 0; } int sc_pkcs15init_delete_object(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; struct sc_path path; struct sc_pkcs15_df *df; int r = 0, stored_in_ef = 0; LOG_FUNC_CALLED(ctx); switch(obj->type & SC_PKCS15_TYPE_CLASS_MASK) { case SC_PKCS15_TYPE_PUBKEY: path = ((struct sc_pkcs15_pubkey_info *)obj->data)->path; break; case SC_PKCS15_TYPE_PRKEY: path = ((struct sc_pkcs15_prkey_info *)obj->data)->path; break; case SC_PKCS15_TYPE_CERT: path = ((struct sc_pkcs15_cert_info *)obj->data)->path; break; case SC_PKCS15_TYPE_DATA_OBJECT: path = ((struct sc_pkcs15_data_info *)obj->data)->path; break; default: return SC_ERROR_NOT_SUPPORTED; } sc_log(ctx, "delete object(type:%X) with path(type:%X,%s)", obj->type, path.type, sc_print_path(&path)); if (profile->ops->delete_object != NULL) { /* If there's a card-specific way to delete objects, use it. */ r = profile->ops->delete_object(profile, p15card, obj, &path); if (r != SC_ERROR_NOT_SUPPORTED) LOG_TEST_RET(ctx, r, "Card specific delete object failed"); } if (profile->ops->delete_object == NULL || r == SC_ERROR_NOT_SUPPORTED) { if (path.len || path.aid.len) { r = sc_select_file(p15card->card, &path, &file); if (r != SC_ERROR_FILE_NOT_FOUND) LOG_TEST_RET(ctx, r, "select object path failed"); stored_in_ef = (file->type != SC_FILE_TYPE_DF); sc_file_free(file); } /* If the object is stored in a normal EF, try to delete the EF. */ if (r == SC_SUCCESS && stored_in_ef) { r = sc_pkcs15init_delete_by_path(profile, p15card, &path); LOG_TEST_RET(ctx, r, "Failed to delete object by path"); } } if (profile->ops->emu_update_any_df) { r = profile->ops->emu_update_any_df(profile, p15card, SC_AC_OP_ERASE, obj); LOG_TEST_RET(ctx, r, "'ERASE' update DF failed"); } /* Get the DF we're part of. If there's no DF, fine, we haven't been added yet. */ df = obj->df; if (df) { /* Unlink the object and update the DF */ sc_pkcs15_remove_object(p15card, obj); sc_pkcs15_free_object(obj); } if (!profile->ops->emu_update_any_df) r = sc_pkcs15init_update_any_df(p15card, profile, df, 0); /* mark card as dirty */ profile->dirty = 1; LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15init_update_certificate(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *obj, const unsigned char *rawcert, size_t certlen) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *file = NULL; struct sc_path *path = &((struct sc_pkcs15_cert_info *)obj->data)->path; int r; LOG_FUNC_CALLED(ctx); r = sc_select_file(p15card->card, path, &file); LOG_TEST_RET(ctx, r, "Failed to select cert file"); /* If the new cert doesn't fit in the EF, delete it and make the same, but bigger EF */ if (file->size != certlen) { struct sc_file *parent = NULL; r = sc_pkcs15init_delete_by_path(profile, p15card, path); if (r < 0) goto done; file->size = certlen; r = do_select_parent(profile, p15card, file, &parent); if (r < 0) goto done; r = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_CREATE); sc_file_free(parent); if (r < 0) { sc_log(ctx, "'CREATE' authentication failed"); goto done; } /* ensure we are in the correct lifecycle */ r = sc_pkcs15init_set_lifecycle(p15card->card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) goto done; r = sc_create_file(p15card->card, file); if (r < 0) { sc_log(ctx, "Cannot create cert file"); goto done; } } if (!sc_file_get_acl_entry(file, SC_AC_OP_UPDATE)) { struct sc_path tmp_path; /* FCI of selected cert file do not contains ACLs. * For the 'UPDATE' authentication use instead sc_file * instantiated from card profile with default ACLs. */ sc_file_free(file); r = select_object_path(p15card, profile, obj, &tmp_path); if (r < 0) { sc_log(ctx, "Select object path error"); goto done; } r = sc_profile_get_file_by_path(profile, path, &file); if (r < 0) { sc_log(ctx, "Cannot instantiate cert file"); goto done; } } /* Write the new cert */ r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); if (r < 0) { sc_log(ctx, "'UPDATE' authentication failed"); goto done; } r = sc_select_file(p15card->card, path, NULL); if (r < 0) goto done; r = sc_update_binary(p15card->card, 0, rawcert, certlen, 0); if (r < 0) goto done; /* Fill the remaining space in the EF (if any) with zeros */ if (certlen < file->size) { unsigned char *tmp = calloc(file->size - certlen, 1); if (tmp == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto done; } r = sc_update_binary(p15card->card, certlen, tmp, file->size - certlen, 0); free(tmp); if (r < 0) sc_log(ctx, "Update cert file error"); } if (r >= 0) { /* Update the CDF entry */ path = &((struct sc_pkcs15_cert_info *)obj->data)->path; if (file->size != certlen) { path->index = 0; path->count = certlen; } else { path->count = -1; } if (profile->ops->emu_update_any_df) { r = profile->ops->emu_update_any_df(profile, p15card, SC_AC_OP_UPDATE, obj); if (r == SC_ERROR_NOT_SUPPORTED) r = SC_SUCCESS; } else { r = sc_pkcs15init_update_any_df(p15card, profile, obj->df, 0); } if (r < 0) sc_log(ctx, "Failed to update CDF"); } /* mark card as dirty */ profile->dirty = 1; done: if (file) sc_file_free(file); LOG_FUNC_RETURN(ctx, r); } static const char * get_pin_ident_name(int type, int reference) { switch (type) { case SC_AC_CHV: return "PIN"; case SC_AC_PRO: return "secure messaging key"; case SC_AC_AUT: return "authentication key"; case SC_AC_SEN: return "security environment"; case SC_AC_IDA: return "PKCS#15 reference"; case SC_AC_SCB: return "SCB byte in IAS/ECC"; case SC_AC_SYMBOLIC: switch (reference) { case SC_PKCS15INIT_USER_PIN: return "user PIN"; case SC_PKCS15INIT_SO_PIN: return "SO PIN"; case SC_PKCS15INIT_USER_PUK: return "user PUK"; case SC_PKCS15INIT_SO_PUK: return "SO PUK"; } } return "authentication data"; } static int sc_pkcs15init_get_transport_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, int type, int reference, unsigned char *pinbuf, size_t *pinsize) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *pin_obj = NULL; struct sc_pkcs15_auth_info auth_info; struct sc_cardctl_default_key data; size_t defsize = 0; unsigned char defbuf[0x100]; int rv; LOG_FUNC_CALLED(ctx); data.method = type; data.key_ref = reference; data.len = sizeof(defbuf); data.key_data = defbuf; rv = sc_card_ctl(p15card->card, SC_CARDCTL_GET_DEFAULT_KEY, &data); if (rv >= 0) defsize = data.len; if (callbacks.get_key) { rv = callbacks.get_key(profile, type, reference, defbuf, defsize, pinbuf, pinsize); } else if (rv >= 0) { if (*pinsize < defsize) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Get transport key error"); memcpy(pinbuf, data.key_data, data.len); *pinsize = data.len; } memset(&auth_info, 0, sizeof(auth_info)); auth_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; auth_info.auth_method = type; auth_info.attrs.pin.reference = reference; auth_info.attrs.pin.stored_length = *pinsize; auth_info.attrs.pin.max_length = *pinsize; auth_info.attrs.pin.min_length = *pinsize; pin_obj = sc_pkcs15init_new_object(SC_PKCS15_TYPE_AUTH_PIN, "Default transport key", NULL, &auth_info); if (!pin_obj) LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate AUTH object"); rv = sc_pkcs15_add_object(p15card, pin_obj); LOG_TEST_RET(ctx, rv, "Cannot add PKCS#15 AUTH object"); sc_pkcs15_pincache_add(p15card, pin_obj, pinbuf, *pinsize); LOG_FUNC_RETURN(ctx, rv); } /* * PIN verification */ int sc_pkcs15init_verify_secret(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *file, unsigned int type, int reference) { struct sc_context *ctx = p15card->card->ctx; struct sc_pkcs15_object *pin_obj = NULL; struct sc_pkcs15_auth_info auth_info; struct sc_path *path; int r, use_pinpad = 0, pin_id = -1; const char *ident, *label = NULL; unsigned char pinbuf[0x100]; size_t pinsize = sizeof(pinbuf); LOG_FUNC_CALLED(ctx); path = file? &file->path : NULL; ident = get_pin_ident_name(type, reference); sc_log(ctx, "get and verify PIN('%s',type:0x%X,reference:0x%X)", ident, type, reference); if (type == SC_AC_SEN) { r = sc_card_ctl(p15card->card, SC_CARDCTL_GET_CHV_REFERENCE_IN_SE, (void *)(&reference)); sc_log(ctx, "Card CTL(GET_CHV_REFERENCE_IN_SE) returned %i", r); if (r > 0) { sc_log(ctx, "CHV(ref:%i) found in SE(ref:%i)", r, reference); type = SC_AC_CHV; reference = r; } else if (r != SC_ERROR_NOT_SUPPORTED) LOG_TEST_RET(ctx, r, "Card CTL error: cannot get CHV reference"); } memset(&auth_info, 0, sizeof(auth_info)); auth_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; auth_info.auth_method = type; auth_info.attrs.pin.reference = reference; pin_id = sc_pkcs15init_get_pin_reference(p15card, profile, type, reference); sc_log(ctx, "found PIN reference %i", pin_id); if (type == SC_AC_SYMBOLIC) { if (pin_id == -1) LOG_FUNC_RETURN(ctx, SC_SUCCESS); reference = pin_id; type = SC_AC_CHV; sc_log(ctx, "Symbolic PIN resolved to PIN(type:CHV,reference:%i)", type, reference); } if (p15card) { if (path && path->len) { struct sc_path tmp_path = *path; int iter; r = SC_ERROR_OBJECT_NOT_FOUND; for (iter = tmp_path.len/2; iter >= 0 && r == SC_ERROR_OBJECT_NOT_FOUND; iter--, tmp_path.len -= 2) r = sc_pkcs15_find_pin_by_type_and_reference(p15card, tmp_path.len ? &tmp_path : NULL, type, reference, &pin_obj); } else { r = sc_pkcs15_find_pin_by_type_and_reference(p15card, NULL, type, reference, &pin_obj); } if (!r && pin_obj) { memcpy(&auth_info, pin_obj->data, sizeof(auth_info)); sc_log(ctx, "found PIN object '%s'", pin_obj->label); } } if (pin_obj) { sc_log(ctx, "PIN object '%s'; pin_obj->content.len:%i", pin_obj->label, pin_obj->content.len); if (pin_obj->content.value && pin_obj->content.len) { if (pin_obj->content.len > pinsize) LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "PIN buffer is too small"); memcpy(pinbuf, pin_obj->content.value, pin_obj->content.len); pinsize = pin_obj->content.len; sc_log(ctx, "'ve got '%s' value from cache", ident); goto found; } } if (pin_obj && pin_obj->label[0]) label = pin_obj->label; switch (type) { case SC_AC_CHV: if (callbacks.get_pin) { r = callbacks.get_pin(profile, pin_id, &auth_info, label, pinbuf, &pinsize); sc_log(ctx, "'get_pin' callback returned %i; pinsize:%i", r, pinsize); } break; case SC_AC_SCB: case SC_AC_PRO: pinsize = 0; r = 0; break; default: r = sc_pkcs15init_get_transport_key(profile, p15card, type, reference, pinbuf, &pinsize); break; } if (r == SC_ERROR_OBJECT_NOT_FOUND) { if (p15card->card->reader->capabilities & SC_READER_CAP_PIN_PAD) r = 0, use_pinpad = 1; else r = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; } LOG_TEST_RET(ctx, r, "Failed to get secret"); if (type == SC_AC_PRO) { sc_log(ctx, "No 'verify' for secure messaging"); LOG_FUNC_RETURN(ctx, r); } found: if (pin_obj) { r = sc_pkcs15_verify_pin(p15card, pin_obj, pinsize ? pinbuf : NULL, pinsize); LOG_TEST_RET(ctx, r, "Cannot validate pkcs15 PIN"); } if (file) { r = sc_select_file(p15card->card, &file->path, NULL); LOG_TEST_RET(ctx, r, "Failed to select PIN path"); } if (!pin_obj) { struct sc_pin_cmd_data pin_cmd; memset(&pin_cmd, 0, sizeof(pin_cmd)); pin_cmd.cmd = SC_PIN_CMD_VERIFY; pin_cmd.pin_type = type; pin_cmd.pin_reference = reference; pin_cmd.pin1.data = use_pinpad ? NULL : pinbuf; pin_cmd.pin1.len = use_pinpad ? 0: pinsize; r = sc_pin_cmd(p15card->card, &pin_cmd, NULL); LOG_TEST_RET(ctx, r, "'VERIFY' pin cmd failed"); } LOG_FUNC_RETURN(ctx, r); } /* * Present any authentication info as required by the file. * * Depending on the SC_CARD_CAP_USE_FCI_AC caps file in sc_card_t, * we read the ACs of the file on the card, or rely on the ACL * info for that file in the profile file. * * In the latter case, there's a problem here if e.g. the SO PIN * defined by the profile is optional, and hasn't been set. * On the orther hands, some cards do not return access conditions * in their response to SELECT FILE), so the latter case has been * used in most cards while the first case was added much later. */ int sc_pkcs15init_authenticate(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *file, int op) { struct sc_context *ctx = p15card->card->ctx; const struct sc_acl_entry *acl = NULL; struct sc_file *file_tmp = NULL; int r = 0; LOG_FUNC_CALLED(ctx); sc_log(ctx, "path '%s', op=%u", sc_print_path(&file->path), op); if (p15card->card->caps & SC_CARD_CAP_USE_FCI_AC) { r = sc_select_file(p15card->card, &file->path, &file_tmp); LOG_TEST_RET(ctx, r, "Authentication failed: cannot select file."); acl = sc_file_get_acl_entry(file_tmp, op); } else { acl = sc_file_get_acl_entry(file, op); } sc_log(ctx, "acl %p",acl); for (; r == 0 && acl; acl = acl->next) { if (acl->method == SC_AC_NEVER) { LOG_TEST_RET(ctx, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED, "Authentication failed: never allowed"); } else if (acl->method == SC_AC_NONE) { sc_log(ctx, "always allowed"); break; } else if (acl->method == SC_AC_UNKNOWN) { sc_log(ctx, "unknown acl method"); break; } sc_log(ctx, "verify acl(method:%i,reference:%i)", acl->method, acl->key_ref); r = sc_pkcs15init_verify_secret(profile, p15card, file_tmp ? file_tmp : file, acl->method, acl->key_ref); } if (file_tmp) sc_file_free(file_tmp); LOG_FUNC_RETURN(ctx, r); } static int do_select_parent(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *file, struct sc_file **parent) { struct sc_context *ctx = p15card->card->ctx; struct sc_path path; int r; LOG_FUNC_CALLED(ctx); /* Get the parent's path */ path = file->path; if (path.len >= 2) path.len -= 2; if (!path.len && !path.aid.len) sc_format_path("3F00", &path); /* Select the parent DF. */ *parent = NULL; r = sc_select_file(p15card->card, &path, parent); /* If DF doesn't exist, create it (unless it's the MF, * but then something's badly broken anyway :-) */ if (r == SC_ERROR_FILE_NOT_FOUND && path.len != 2) { r = sc_profile_get_file_by_path(profile, &path, parent); if (r < 0) { sc_log(ctx, "no profile template for DF %s", sc_print_path(&path)); LOG_FUNC_RETURN(ctx, r); } r = sc_pkcs15init_create_file(profile, p15card, *parent); LOG_TEST_RET(ctx, r, "Cannot create parent DF"); r = sc_select_file(p15card->card, &path, NULL); LOG_TEST_RET(ctx, r, "Cannot select parent DF"); } else if (r == SC_SUCCESS && !strcmp(p15card->card->name, "STARCOS SPK 2.3")) { /* in case of starcos spk 2.3 SELECT FILE does not * give us the ACLs => ask the profile */ sc_file_free(*parent); r = sc_profile_get_file_by_path(profile, &path, parent); if (r < 0) { sc_log(ctx, "in StarCOS profile there is no template for DF %s", sc_print_path(&path)); LOG_FUNC_RETURN(ctx, r); } } LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15init_create_file(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *file) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *parent = NULL; int r; LOG_FUNC_CALLED(ctx); sc_log(ctx, "create file '%s'", sc_print_path(&file->path)); /* Select parent DF and verify PINs/key as necessary */ r = do_select_parent(profile, p15card, file, &parent); LOG_TEST_RET(ctx, r, "Cannot create file: select parent error"); r = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_CREATE); LOG_TEST_RET(ctx, r, "Cannot create file: 'CREATE' authentication failed"); /* Fix up the file's ACLs */ r = sc_pkcs15init_fixup_file(profile, p15card, file); LOG_TEST_RET(ctx, r, "Cannot create file: file fixup failed"); /* ensure we are in the correct lifecycle */ r = sc_pkcs15init_set_lifecycle(p15card->card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r != SC_ERROR_NOT_SUPPORTED) LOG_TEST_RET(ctx, r, "Cannot create file: failed to set lifecycle 'ADMIN'"); r = sc_create_file(p15card->card, file); LOG_TEST_RET(ctx, r, "Create file failed"); if (parent) sc_file_free(parent); LOG_FUNC_RETURN(ctx, r); } int sc_pkcs15init_update_file(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *file, void *data, unsigned int datalen) { struct sc_context *ctx = p15card->card->ctx; struct sc_file *selected_file = NULL; void *copy = NULL; int r, need_to_zap = 0; LOG_FUNC_CALLED(ctx); sc_log(ctx, "path:%s; datalen:%i", sc_print_path(&file->path), datalen); r = sc_select_file(p15card->card, &file->path, &selected_file); if (!r) { need_to_zap = 1; } else if (r == SC_ERROR_FILE_NOT_FOUND) { /* Create file if it doesn't exist */ if (file->size < datalen) file->size = datalen; r = sc_pkcs15init_create_file(profile, p15card, file); LOG_TEST_RET(ctx, r, "Failed to create file"); r = sc_select_file(p15card->card, &file->path, &selected_file); LOG_TEST_RET(ctx, r, "Failed to select newly created file"); } else { LOG_TEST_RET(ctx, r, "Failed to select file"); } if (selected_file->size < datalen) { sc_log(ctx, "File %s too small (require %u, have %u)", sc_print_path(&file->path), datalen, selected_file->size); sc_file_free(selected_file); LOG_TEST_RET(ctx, SC_ERROR_FILE_TOO_SMALL, "Update file failed"); } else if (selected_file->size > datalen && need_to_zap) { /* zero out the rest of the file - we may have shrunk * the file contents */ copy = calloc(1, selected_file->size); if (copy == NULL) { sc_file_free(selected_file); return SC_ERROR_OUT_OF_MEMORY; } memcpy(copy, data, datalen); datalen = selected_file->size; data = copy; } /* Present authentication info needed */ r = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); if (r >= 0 && datalen) r = sc_update_binary(p15card->card, 0, (const unsigned char *) data, datalen, 0); if (copy) free(copy); sc_file_free(selected_file); LOG_FUNC_RETURN(ctx, r); } /* * Fix up a file's ACLs by replacing all occurrences of a symbolic * PIN name with the real reference. */ static int sc_pkcs15init_fixup_acls(struct sc_pkcs15_card *p15card, struct sc_file *file, struct sc_acl_entry *so_acl, struct sc_acl_entry *user_acl) { struct sc_context *ctx = p15card->card->ctx; unsigned int op; int r = 0; LOG_FUNC_CALLED(ctx); for (op = 0; r == 0 && op < SC_MAX_AC_OPS; op++) { struct sc_acl_entry acls[SC_MAX_OP_ACS]; const struct sc_acl_entry *acl; const char *what; int added = 0, num, ii; /* First, get original ACLs */ acl = sc_file_get_acl_entry(file, op); for (num = 0; num < SC_MAX_OP_ACS && acl; num++, acl = acl->next) acls[num] = *acl; sc_file_clear_acl_entries(file, op); for (ii = 0; ii < num; ii++) { acl = acls + ii; if (acl->method != SC_AC_SYMBOLIC) goto next; if (acl->key_ref == SC_PKCS15INIT_SO_PIN) { acl = so_acl; what = "SO PIN"; } else if (acl->key_ref == SC_PKCS15INIT_USER_PIN) { acl = user_acl; what = "user PIN"; } else { sc_log(ctx, "ACL references unknown symbolic PIN %d", acl->key_ref); return SC_ERROR_INVALID_ARGUMENTS; } /* If we weren't given a replacement ACL, * leave the original ACL untouched */ if (acl->key_ref == (unsigned int)-1) { sc_log(ctx, "ACL references %s, which is not defined", what); return SC_ERROR_INVALID_ARGUMENTS; } if (acl->method == SC_AC_NONE) continue; next: sc_file_add_acl_entry(file, op, acl->method, acl->key_ref); added++; } if (!added) sc_file_add_acl_entry(file, op, SC_AC_NONE, 0); } LOG_FUNC_RETURN(ctx, r); } /* * Fix up all file ACLs */ int sc_pkcs15init_fixup_file(struct sc_profile *profile, struct sc_pkcs15_card *p15card, struct sc_file *file) { struct sc_context *ctx = profile->card->ctx; struct sc_acl_entry so_acl, user_acl; unsigned int op, needfix = 0; int rv, pin_ref; LOG_FUNC_CALLED(ctx); /* First, loop over all ACLs to find out whether there * are still any symbolic references. */ for (op = 0; op < SC_MAX_AC_OPS; op++) { const struct sc_acl_entry *acl; acl = sc_file_get_acl_entry(file, op); for (; acl; acl = acl->next) if (acl->method == SC_AC_SYMBOLIC) needfix++; } if (!needfix) LOG_FUNC_RETURN(ctx, SC_SUCCESS); pin_ref = sc_pkcs15init_get_pin_reference(p15card, profile, SC_AC_SYMBOLIC, SC_PKCS15INIT_SO_PIN); if (pin_ref < 0) { so_acl.method = SC_AC_NONE; so_acl.key_ref = 0; } else { so_acl.method = SC_AC_CHV; so_acl.key_ref = pin_ref; } pin_ref = sc_pkcs15init_get_pin_reference(p15card, profile, SC_AC_SYMBOLIC, SC_PKCS15INIT_USER_PIN); if (pin_ref < 0) { user_acl.method = SC_AC_NONE; user_acl.key_ref = 0; } else { user_acl.method = SC_AC_CHV; user_acl.key_ref = pin_ref; } sc_log(ctx, "so_acl(method:%X,ref:%X), user_acl(method:%X,ref:%X)", so_acl.method, so_acl.key_ref, user_acl.method, user_acl.key_ref); rv = sc_pkcs15init_fixup_acls(p15card, file, &so_acl, &user_acl); LOG_FUNC_RETURN(ctx, rv); } static int sc_pkcs15init_get_pin_path(struct sc_pkcs15_card *p15card, struct sc_pkcs15_id *auth_id, struct sc_path *path) { struct sc_pkcs15_object *obj; int r; r = sc_pkcs15_find_pin_by_auth_id(p15card, auth_id, &obj); if (r < 0) return r; *path = ((struct sc_pkcs15_auth_info *) obj->data)->path; return SC_SUCCESS; } int sc_pkcs15init_get_pin_info(struct sc_profile *profile, int id, struct sc_pkcs15_auth_info *pin) { sc_profile_get_pin_info(profile, id, pin); return SC_SUCCESS; } int sc_pkcs15init_get_manufacturer(struct sc_profile *profile, const char **res) { *res = profile->p15_spec->tokeninfo->manufacturer_id; return SC_SUCCESS; } int sc_pkcs15init_get_serial(struct sc_profile *profile, const char **res) { *res = profile->p15_spec->tokeninfo->serial_number; return SC_SUCCESS; } int sc_pkcs15init_set_serial(struct sc_profile *profile, const char *serial) { if (profile->p15_spec->tokeninfo->serial_number) free(profile->p15_spec->tokeninfo->serial_number); profile->p15_spec->tokeninfo->serial_number = strdup(serial); return SC_SUCCESS; } /* * Card specific sanity check procedure. */ int sc_pkcs15init_sanity_check(struct sc_pkcs15_card *p15card, struct sc_profile *profile) { struct sc_context *ctx = p15card->card->ctx; int rv = SC_ERROR_NOT_SUPPORTED; LOG_FUNC_CALLED(ctx); if (profile->ops->sanity_check) rv = profile->ops->sanity_check(profile, p15card); LOG_FUNC_RETURN(ctx, rv); } static int sc_pkcs15init_qualify_pin(struct sc_card *card, const char *pin_name, unsigned int pin_len, struct sc_pkcs15_auth_info *auth_info) { struct sc_context *ctx = card->ctx; struct sc_pkcs15_pin_attributes *pin_attrs; if (pin_len == 0 || auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_SUCCESS; pin_attrs = &auth_info->attrs.pin; if (pin_len < pin_attrs->min_length) { sc_log(ctx, "%s too short (min length %u)", pin_name, pin_attrs->min_length); return SC_ERROR_WRONG_LENGTH; } if (pin_len > pin_attrs->max_length) { sc_log(ctx, "%s too long (max length %u)", pin_name, pin_attrs->max_length); return SC_ERROR_WRONG_LENGTH; } return SC_SUCCESS; } /* * Get the list of options from the card, if it specifies them */ static int sc_pkcs15init_read_info(struct sc_card *card, struct sc_profile *profile) { struct sc_path path; struct sc_file *file = NULL; unsigned char *mem = NULL; size_t len = 0; int r; sc_format_path(OPENSC_INFO_FILEPATH, &path); r = sc_select_file(card, &path, &file); if (r >= 0) { len = file->size; sc_file_free(file); mem = malloc(len); if (mem != NULL) r = sc_read_binary(card, 0, mem, len, 0); else r = SC_ERROR_OUT_OF_MEMORY; } else { r = 0; } if (r >= 0) r = sc_pkcs15init_parse_info(card, mem, len, profile); if (mem) free(mem); return r; } static int set_info_string(char **strp, const u8 *p, size_t len) { char *s; if (!(s = malloc(len+1))) return SC_ERROR_OUT_OF_MEMORY; memcpy(s, p, len); s[len] = '\0'; if (*strp) free(*strp); *strp = s; return SC_SUCCESS; } /* * Parse OpenSC Info file. We rudely clobber any information * given on the command line. * * passed is a pointer (p) to (len) bytes. Those bytes contain * one or several tag-length-value constructs, where tag and * length are both single bytes. a final 0x00 or 0xff byte * (with or without len byte) is ok. */ static int sc_pkcs15init_parse_info(struct sc_card *card, const unsigned char *p, size_t len, struct sc_profile *profile) { unsigned char tag; const unsigned char *end; unsigned int nopts = 0; size_t n; if ((p == NULL) || (len == 0)) return 0; end = p + (len - 1); while (p < end) { /* more bytes to look at */ int r = 0; tag = *p; p++; if ((tag == 0) || (tag == 0xff) || (p >= end)) break; n = *p; p++; if (p >= end || p + n > end) /* invalid length byte n */ goto error; switch (tag) { case OPENSC_INFO_TAG_PROFILE: r = set_info_string(&profile->name, p, n); if (r < 0) return r; break; case OPENSC_INFO_TAG_OPTION: if (nopts >= SC_PKCS15INIT_MAX_OPTIONS - 1) { sc_log(card->ctx, "Too many options in OpenSC Info file"); return SC_ERROR_PKCS15INIT; } r = set_info_string(&profile->options[nopts], p, n); if (r < 0) return r; profile->options[++nopts] = NULL; break; default: /* Unknown options ignored */ ; } p += n; } return 0; error: sc_log(card->ctx, "OpenSC info file corrupted"); return SC_ERROR_PKCS15INIT; } static int do_encode_string(unsigned char **memp, unsigned char *end, unsigned char tag, const char *s) { unsigned char *p = *memp; int n; n = s? strlen(s) : 0; if (n > 255) return SC_ERROR_BUFFER_TOO_SMALL; if (p + 2 + n > end) return SC_ERROR_BUFFER_TOO_SMALL; *p++ = tag; *p++ = n; memcpy(p, s, n); *memp = p + n; return 0; } static int sc_pkcs15init_write_info(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *pin_obj) { struct sc_file *file = NULL, *df = profile->df_info->file; unsigned char buffer[128], *p, *end; unsigned int method; unsigned long key_ref; int n, r; if (profile->ops->emu_write_info) return profile->ops->emu_write_info(profile, p15card, pin_obj); memset(buffer, 0, sizeof(buffer)); file = sc_file_new(); file->path.type = SC_PATH_TYPE_PATH; memcpy(file->path.value, df->path.value, df->path.len); file->path.len = df->path.len; sc_append_file_id(&file->path, OPENSC_INFO_FILEID); file->type = SC_FILE_TYPE_WORKING_EF; file->ef_structure = SC_FILE_EF_TRANSPARENT; file->id = OPENSC_INFO_FILEID; file->size = sizeof(buffer); if (pin_obj != NULL) { method = SC_AC_CHV; key_ref = ((struct sc_pkcs15_auth_info *) pin_obj->data)->attrs.pin.reference; } else { method = SC_AC_NONE; /* Unprotected */ key_ref = 0; } for (n = 0; n < SC_MAX_AC_OPS; n++) { if (n == SC_AC_OP_READ) sc_file_add_acl_entry(file, n, SC_AC_NONE, 0); else sc_file_add_acl_entry(file, n, method, key_ref); } p = buffer; end = buffer + sizeof(buffer); r = do_encode_string(&p, end, OPENSC_INFO_TAG_PROFILE, profile->name); for (n = 0; r >= 0 && profile->options[n]; n++) r = do_encode_string(&p, end, OPENSC_INFO_TAG_OPTION, profile->options[n]); if (r >= 0) r = sc_pkcs15init_update_file(profile, p15card, file, buffer, file->size); sc_file_free(file); return r; }