opensc/src/pkcs15init/pkcs15-lib.c

3935 lines
112 KiB
C

/*
* 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-<cardname>.c
*
* Copyright (C) 2002, Olaf Kirch <okir@lst.de>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <time.h>
#ifdef HAVE_GETTIMEOFDAY
#include <sys/time.h>
#endif
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <assert.h>
#ifdef ENABLE_OPENSSL
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/pkcs12.h>
#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 key;
int keybits, 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) {
/* FIXME free object with sc_pkcs15init_delete_object */
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; ii<nn_objs; ii++) {
struct sc_pkcs15_auth_info *auth_info = (struct sc_pkcs15_auth_info *)auth_objs[ii]->data;
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 = &params->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; ii<nn_objs; ii++) {
r = get_object_path_from_object(objs[ii], &obj_path);
LOG_TEST_RET(ctx, r, "Failed to get object path from pkcs15 object");
if (obj_path.len != file->path.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;
}