742b0ea341
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3963 c6295689-39f2-0310-b995-f0e70906c6a9
4016 lines
109 KiB
C
4016 lines
109 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
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
#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 <ltdl.h>
|
|
#include <opensc/pkcs15.h>
|
|
#include "profile.h"
|
|
#include "pkcs15-init.h"
|
|
#include <opensc/cardctl.h>
|
|
#include <opensc/log.h>
|
|
#include <compat_strlcpy.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_ACCESS_FLAGS 0x1d
|
|
#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
|
|
|
|
/* Handle encoding of PKCS15 on the card */
|
|
typedef int (*pkcs15_encoder)(sc_context_t *,
|
|
struct sc_pkcs15_card *, u8 **, size_t *);
|
|
|
|
static int sc_pkcs15init_store_data(struct sc_pkcs15_card *,
|
|
struct sc_profile *, sc_pkcs15_object_t *,
|
|
sc_pkcs15_id_t *,
|
|
sc_pkcs15_der_t *, sc_path_t *);
|
|
static size_t sc_pkcs15init_keybits(sc_pkcs15_bignum_t *);
|
|
|
|
static int sc_pkcs15init_update_dir(struct sc_pkcs15_card *,
|
|
struct sc_profile *profile,
|
|
sc_app_info_t *app);
|
|
static int sc_pkcs15init_update_tokeninfo(struct sc_pkcs15_card *,
|
|
struct sc_profile *profile);
|
|
static int sc_pkcs15init_update_odf(struct sc_pkcs15_card *,
|
|
struct sc_profile *profile);
|
|
static sc_pkcs15_object_t *sc_pkcs15init_new_object(int type, const char *label,
|
|
sc_pkcs15_id_t *auth_id, void *data);
|
|
static int sc_pkcs15init_add_object(struct sc_pkcs15_card *,
|
|
struct sc_profile *profile,
|
|
unsigned int df_type,
|
|
struct sc_pkcs15_object *);
|
|
static int sc_pkcs15init_remove_object(sc_pkcs15_card_t *,
|
|
sc_profile_t *, sc_pkcs15_object_t *);
|
|
static int sc_pkcs15init_map_usage(unsigned long, int);
|
|
static int set_so_pin_from_card(struct sc_pkcs15_card *,
|
|
struct sc_profile *);
|
|
static int set_user_pin_from_authid(struct sc_pkcs15_card *,
|
|
struct sc_profile *, struct sc_pkcs15_id *);
|
|
static int do_select_parent(struct sc_profile *, sc_card_t *,
|
|
sc_file_t *, sc_file_t **);
|
|
static int sc_pkcs15init_create_pin(sc_pkcs15_card_t *, sc_profile_t *,
|
|
sc_pkcs15_object_t *, struct sc_pkcs15init_pinargs *);
|
|
static int check_key_size(sc_card_t *card, unsigned int alg,
|
|
unsigned int bits);
|
|
static int check_key_compatibility(struct sc_pkcs15_card *,
|
|
struct sc_pkcs15_prkey *, unsigned int,
|
|
unsigned int, unsigned int);
|
|
static int prkey_fixup(sc_pkcs15_card_t *, sc_pkcs15_prkey_t *);
|
|
static int prkey_bits(sc_pkcs15_card_t *, sc_pkcs15_prkey_t *);
|
|
static int prkey_pkcs15_algo(sc_pkcs15_card_t *, sc_pkcs15_prkey_t *);
|
|
static int select_intrinsic_id(sc_pkcs15_card_t *, struct sc_profile *,
|
|
int, sc_pkcs15_id_t *, void *);
|
|
static int select_id(struct sc_pkcs15_card *, int, struct sc_pkcs15_id *);
|
|
static int select_object_path(sc_pkcs15_card_t *, sc_profile_t *,
|
|
sc_pkcs15_object_t *, sc_pkcs15_id_t *, sc_path_t *);
|
|
static int sc_pkcs15init_get_pin_path(sc_pkcs15_card_t *,
|
|
sc_pkcs15_id_t *, sc_path_t *);
|
|
static int sc_pkcs15init_qualify_pin(sc_card_t *, const char *,
|
|
unsigned int, sc_pkcs15_pin_info_t *);
|
|
static struct sc_pkcs15_df * find_df_by_type(struct sc_pkcs15_card *,
|
|
unsigned int);
|
|
static int sc_pkcs15init_read_info(sc_card_t *card, sc_profile_t *);
|
|
static int sc_pkcs15init_parse_info(sc_card_t *, const u8 *, size_t, sc_profile_t *);
|
|
static int sc_pkcs15init_write_info(sc_card_t *card, sc_profile_t *,
|
|
sc_pkcs15_object_t *pin_obj);
|
|
#if 0
|
|
static int sc_pkcs15init_read_unusedspace(sc_pkcs15_card_t *);
|
|
static int sc_pkcs15init_update_unusedspace(sc_pkcs15_card_t *, sc_profile_t *);
|
|
static sc_pkcs15_unusedspace_t *merge_paths(sc_pkcs15_unusedspace_t *, const sc_path_t *);
|
|
static int sc_pkcs15init_add_unusedspace(sc_pkcs15_card_t *,
|
|
sc_profile_t *, const sc_path_t *, const sc_pkcs15_id_t *);
|
|
static int sc_pkcs15init_remove_unusedspace(sc_pkcs15_card_t *,
|
|
sc_profile_t *, const sc_path_t *);
|
|
#endif
|
|
|
|
|
|
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 },
|
|
{ "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 },
|
|
{ "rutoken_ecp", (void *) sc_pkcs15init_get_rtecp_ops },
|
|
{ "westcos", (void *) sc_pkcs15init_get_westcos_ops },
|
|
{ "myeid", (void *) sc_pkcs15init_get_myeid_ops },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
static struct sc_pkcs15init_callbacks callbacks = {
|
|
NULL,
|
|
NULL,
|
|
};
|
|
|
|
/*
|
|
* 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(sc_card_t *card, char *buffer, size_t size)
|
|
{
|
|
sc_context_t *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(sc_context_t *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_debug(ctx, "unable to locate pkcs15init driver for '%s'\n", name);
|
|
}
|
|
return libname;
|
|
}
|
|
|
|
static void *load_dynamic_driver(sc_context_t *ctx, void **dll,
|
|
const char *name)
|
|
{
|
|
const char *version, *libname;
|
|
lt_dlhandle handle;
|
|
void *(*modinit)(const char *) = NULL;
|
|
const char *(*modversion)(void) = NULL;
|
|
|
|
libname = find_library(ctx, name);
|
|
if (!libname)
|
|
return NULL;
|
|
handle = lt_dlopen(libname);
|
|
if (handle == NULL) {
|
|
sc_debug(ctx, "Module %s: cannot load '%s' library: %s\n", name, libname, lt_dlerror());
|
|
return NULL;
|
|
}
|
|
|
|
/* verify correctness of module */
|
|
modinit = (void *(*)(const char *)) lt_dlsym(handle, "sc_module_init");
|
|
modversion = (const char *(*)(void)) lt_dlsym(handle, "sc_driver_version");
|
|
if (modinit == NULL || modversion == NULL) {
|
|
sc_debug(ctx, "dynamic library '%s' is not a OpenSC module\n",libname);
|
|
lt_dlclose(handle);
|
|
return NULL;
|
|
}
|
|
/* verify module version */
|
|
version = modversion();
|
|
if (version == NULL || strncmp(version, "0.9.", strlen("0.9.")) > 0) {
|
|
sc_debug(ctx,"dynamic library '%s': invalid module version\n",libname);
|
|
lt_dlclose(handle);
|
|
return NULL;
|
|
}
|
|
*dll = handle;
|
|
sc_debug(ctx, "successfully loaded pkcs15init driver '%s'\n", name);
|
|
|
|
return modinit(name);
|
|
}
|
|
|
|
/*
|
|
* Set up profile
|
|
*/
|
|
int
|
|
sc_pkcs15init_bind(sc_card_t *card, const char *name,
|
|
const char *profile_option,
|
|
struct sc_profile **result)
|
|
{
|
|
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;
|
|
|
|
/* Put the card into administrative mode */
|
|
r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN);
|
|
if (r < 0 && r != SC_ERROR_NOT_SUPPORTED)
|
|
return r;
|
|
|
|
profile = sc_profile_new();
|
|
profile->card = card;
|
|
profile->cbs = &callbacks;
|
|
|
|
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_debug(card->ctx, "Unsupported card driver %s", driver);
|
|
sc_profile_free(profile);
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
}
|
|
|
|
if ((r = sc_pkcs15init_read_info(card, profile)) < 0) {
|
|
sc_profile_free(profile);
|
|
return r;
|
|
}
|
|
|
|
/* 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));
|
|
}
|
|
|
|
if ((r = sc_profile_load(profile, profile->name)) < 0
|
|
|| (r = sc_profile_load(profile, card_profile)) < 0
|
|
|| (r = sc_profile_finish(profile)) < 0) {
|
|
sc_debug(card->ctx, "Failed to load profile: %s\n", sc_strerror(r));
|
|
sc_profile_free(profile);
|
|
return r;
|
|
}
|
|
|
|
*result = profile;
|
|
return r;
|
|
}
|
|
|
|
void
|
|
sc_pkcs15init_unbind(struct sc_profile *profile)
|
|
{
|
|
int r;
|
|
struct sc_context *ctx = profile->card->ctx;
|
|
|
|
if (profile->dirty != 0 && profile->p15_data != NULL && profile->pkcs15.do_last_update) {
|
|
r = sc_pkcs15init_update_tokeninfo(profile->p15_data, profile);
|
|
if (r < 0)
|
|
sc_debug(ctx, "Failed to update TokenInfo: %s\n", sc_strerror(r));
|
|
}
|
|
if (profile->dll)
|
|
lt_dlclose(profile->dll);
|
|
sc_profile_free(profile);
|
|
}
|
|
|
|
void
|
|
sc_pkcs15init_set_p15card(sc_profile_t *profile,
|
|
sc_pkcs15_card_t *p15card)
|
|
{
|
|
profile->p15_data = p15card;
|
|
}
|
|
|
|
/*
|
|
* Set the card's lifecycle
|
|
*/
|
|
int
|
|
sc_pkcs15init_set_lifecycle(sc_card_t *card, int lcycle)
|
|
{
|
|
return sc_card_ctl(card, SC_CARDCTL_LIFECYCLE_SET, &lcycle);
|
|
}
|
|
|
|
/*
|
|
* Erase the card
|
|
*/
|
|
int
|
|
sc_pkcs15init_erase_card(sc_card_t *card, struct sc_profile *profile)
|
|
{
|
|
/* Make sure we set the SO PIN reference in the key cache */
|
|
if (sc_keycache_find_named_pin(NULL, SC_PKCS15INIT_SO_PIN) == -1) {
|
|
struct sc_pkcs15_card *p15card = NULL;
|
|
|
|
if (sc_pkcs15_bind(card, &p15card) >= 0) {
|
|
/* result of set_so_pin_from_card ignored */
|
|
set_so_pin_from_card(p15card, profile);
|
|
profile->p15_data = p15card;
|
|
}
|
|
}
|
|
if (profile->ops->erase_card == NULL)
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
return profile->ops->erase_card(profile, card);
|
|
}
|
|
|
|
int
|
|
sc_pkcs15init_erase_card_recursively(sc_card_t *card,
|
|
struct sc_profile *profile,
|
|
int so_pin_ref)
|
|
{
|
|
struct sc_pkcs15_card *p15orig = profile->p15_data;
|
|
struct sc_file *df = profile->df_info->file, *dir;
|
|
int r;
|
|
|
|
/* Make sure we set the SO PIN reference in the key cache */
|
|
if (sc_keycache_find_named_pin(NULL, SC_PKCS15INIT_SO_PIN) == -1) {
|
|
struct sc_pkcs15_card *p15card = NULL;
|
|
|
|
if (sc_pkcs15_bind(card, &p15card) >= 0) {
|
|
/* result of set_so_pin_from_card ignored */
|
|
set_so_pin_from_card(p15card, profile);
|
|
profile->p15_data = p15card;
|
|
}
|
|
}
|
|
|
|
/* 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(card, profile, dir);
|
|
sc_file_free(dir);
|
|
if (r < 0 && r != SC_ERROR_FILE_NOT_FOUND)
|
|
goto out;
|
|
}
|
|
|
|
r = sc_select_file(card, &df->path, &df);
|
|
if (r >= 0) {
|
|
r = sc_pkcs15init_rmdir(card, profile, df);
|
|
sc_file_free(df);
|
|
}
|
|
if (r == SC_ERROR_FILE_NOT_FOUND)
|
|
r = 0;
|
|
|
|
out: /* Forget any cached keys, the objects on card are all gone. */
|
|
sc_keycache_forget_key(NULL, -1, -1);
|
|
|
|
sc_free_apps(card);
|
|
if (profile->p15_data != p15orig) {
|
|
sc_pkcs15_unbind(profile->p15_data);
|
|
profile->p15_data = p15orig;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
int sc_pkcs15init_delete_by_path(struct sc_profile *profile,
|
|
struct sc_card *card, const sc_path_t *file_path)
|
|
{
|
|
sc_file_t *parent, *file;
|
|
sc_path_t path;
|
|
int r;
|
|
|
|
if (file_path->len >= 2) {
|
|
/* Select the parent DF */
|
|
path = *file_path;
|
|
path.len -= 2;
|
|
r = sc_select_file(card, &path, &parent);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sc_pkcs15init_authenticate(profile, card, parent, SC_AC_OP_DELETE);
|
|
sc_file_free(parent);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
/* Select the file itself */
|
|
path = *file_path;
|
|
r = sc_select_file(card, &path, &file);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sc_pkcs15init_authenticate(profile, card, file, SC_AC_OP_ERASE);
|
|
sc_file_free(file);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
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;
|
|
|
|
r = sc_delete_file(card, &path);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* 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(sc_card_t *card, struct sc_profile *profile,
|
|
sc_file_t *df)
|
|
{
|
|
u8 buffer[1024];
|
|
struct sc_path path;
|
|
struct sc_file *file, *parent;
|
|
int r = 0, nfids;
|
|
char pbuf[SC_MAX_PATH_STRING_SIZE];
|
|
|
|
if (df == NULL)
|
|
return SC_ERROR_INTERNAL;
|
|
r = sc_path_print(pbuf, sizeof(pbuf), &df->path);
|
|
if (r != SC_SUCCESS)
|
|
pbuf[0] = '\0';
|
|
|
|
sc_debug(card->ctx, "sc_pkcs15init_rmdir(%s)\n", pbuf);
|
|
|
|
if (df->type == SC_FILE_TYPE_DF) {
|
|
r = sc_pkcs15init_authenticate(profile, card, df,
|
|
SC_AC_OP_LIST_FILES);
|
|
if (r < 0)
|
|
return r;
|
|
r = sc_list_files(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(card, &path, &file);
|
|
if (r < 0) {
|
|
if (r == SC_ERROR_FILE_NOT_FOUND)
|
|
continue;
|
|
break;
|
|
}
|
|
r = sc_pkcs15init_rmdir(card, 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(card, &path, &parent);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sc_pkcs15init_authenticate(profile, card, df, SC_AC_OP_DELETE);
|
|
if (r < 0) {
|
|
sc_file_free(parent);
|
|
return r;
|
|
}
|
|
r = sc_pkcs15init_authenticate(profile, card, 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(card, SC_CARDCTRL_LIFECYCLE_ADMIN);
|
|
if (r < 0 && r != SC_ERROR_NOT_SUPPORTED)
|
|
return r;
|
|
|
|
r = sc_delete_file(card, &path);
|
|
return r;
|
|
}
|
|
|
|
int
|
|
sc_pkcs15init_finalize_card(sc_card_t *card, struct sc_profile *profile)
|
|
{
|
|
if (profile->ops->finalize_card == NULL)
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
return profile->ops->finalize_card(card);
|
|
}
|
|
|
|
/*
|
|
* Initialize the PKCS#15 application
|
|
*/
|
|
int
|
|
sc_pkcs15init_add_app(sc_card_t *card, struct sc_profile *profile,
|
|
struct sc_pkcs15init_initargs *args)
|
|
{
|
|
sc_context_t *ctx = card->ctx;
|
|
sc_pkcs15_card_t *p15spec = profile->p15_spec;
|
|
sc_pkcs15_pin_info_t pin_info, puk_info;
|
|
sc_pkcs15_object_t *pin_obj = NULL;
|
|
sc_app_info_t *app;
|
|
sc_file_t *df = profile->df_info->file;
|
|
int r;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
p15spec->card = card;
|
|
|
|
/* Add user PINs profile infos to the profile->pin_list. */
|
|
/* TODO: what for ? */
|
|
sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &puk_info);
|
|
sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &puk_info);
|
|
|
|
if (card->app_count >= SC_MAX_CARD_APPS)
|
|
SC_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_info);
|
|
r = sc_pkcs15init_qualify_pin(card, "SO PIN", args->so_pin_len, &pin_info);
|
|
SC_TEST_RET(ctx, r, "Failed to qulify SO PIN");
|
|
|
|
/* Path encoded only for local SO PIN */
|
|
if (pin_info.flags & SC_PKCS15_PIN_FLAG_LOCAL)
|
|
pin_info.path = df->path;
|
|
|
|
/* Select the PIN reference */
|
|
if (profile->ops->select_pin_reference) {
|
|
r = profile->ops->select_pin_reference(profile, card, &pin_info);
|
|
SC_TEST_RET(ctx, r, "Failed to select card specific PIN reference");
|
|
|
|
if (pin_info.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
|
|
sc_keycache_set_pin_name(&pin_info.path,
|
|
pin_info.reference, SC_PKCS15INIT_SO_PIN);
|
|
else
|
|
sc_keycache_set_pin_name(&pin_info.path,
|
|
pin_info.reference, SC_PKCS15INIT_USER_PIN);
|
|
}
|
|
|
|
sc_profile_get_pin_info(profile, SC_PKCS15INIT_SO_PUK, &puk_info);
|
|
r = sc_pkcs15init_qualify_pin(card, "SO PUK", args->so_puk_len, &puk_info);
|
|
SC_TEST_RET(ctx, r, "Failed to qulify SO PUK");
|
|
|
|
if (!(pin_label = args->so_pin_label)) {
|
|
if (pin_info.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
|
|
pin_label = "Security Officer PIN";
|
|
else
|
|
pin_label = "User PIN";
|
|
}
|
|
|
|
if (args->so_puk_len == 0)
|
|
pin_info.flags |= SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED;
|
|
|
|
pin_obj = sc_pkcs15init_new_object(SC_PKCS15_TYPE_AUTH_PIN,
|
|
pin_label, NULL, &pin_info);
|
|
}
|
|
|
|
/* Perform card-specific initialization */
|
|
if (profile->ops->init_card) {
|
|
r = profile->ops->init_card(profile, card);
|
|
if (r < 0 && pin_obj)
|
|
sc_pkcs15_free_object(pin_obj);
|
|
SC_TEST_RET(ctx, r, "Card specific init failed");
|
|
}
|
|
|
|
/* Create the application DF and store the PINs */
|
|
if (profile->ops->create_dir) {
|
|
/* Create the application directory */
|
|
r = profile->ops->create_dir(profile, card, df);
|
|
|
|
/* Set the SO PIN */
|
|
if (r >= 0 && pin_obj) {
|
|
r = profile->ops->create_pin(profile, card, df, pin_obj,
|
|
args->so_pin, args->so_pin_len,
|
|
args->so_puk, args->so_puk_len);
|
|
}
|
|
} else {
|
|
/* Old style API */
|
|
r = profile->ops->init_app(profile, card, &pin_info,
|
|
args->so_pin, args->so_pin_len,
|
|
args->so_puk, args->so_puk_len);
|
|
}
|
|
|
|
if (r < 0 && pin_obj)
|
|
sc_pkcs15_free_object(pin_obj);
|
|
SC_TEST_RET(ctx, r, "Card specific create application DF failed");
|
|
|
|
/* Put the new SO pin in the key cache (note: in case
|
|
* of the "onepin" profile store it as a normal pin) */
|
|
if (args->so_pin_len && !(pin_info.flags & SC_PKCS15_PIN_FLAG_SO_PIN))
|
|
sc_keycache_put_key(&df->path,
|
|
SC_AC_SYMBOLIC,
|
|
SC_PKCS15INIT_USER_PIN,
|
|
args->so_pin,
|
|
args->so_pin_len);
|
|
else
|
|
sc_keycache_put_key(&df->path,
|
|
SC_AC_SYMBOLIC,
|
|
SC_PKCS15INIT_SO_PIN,
|
|
args->so_pin,
|
|
args->so_pin_len);
|
|
|
|
/* 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 = (sc_app_info_t *)calloc(1, sizeof(*app));
|
|
if (app == NULL)
|
|
SC_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Failed to allocate application info");
|
|
app->path = p15spec->file_app->path;
|
|
if (p15spec->file_app->namelen <= SC_MAX_AID_SIZE) {
|
|
app->aid_len = p15spec->file_app->namelen;
|
|
memcpy(app->aid, p15spec->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 */
|
|
sc_serial_number_t 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 (p15spec->label)
|
|
free(p15spec->label);
|
|
p15spec->label = strdup(args->label);
|
|
}
|
|
app->label = strdup(p15spec->label);
|
|
|
|
/* XXX: encode the DDO? */
|
|
|
|
/* See if we've set an SO PIN */
|
|
if (pin_obj) {
|
|
r = sc_pkcs15init_add_object(p15spec, profile,
|
|
SC_PKCS15_AODF, pin_obj);
|
|
} else {
|
|
r = sc_pkcs15init_add_object(p15spec, profile,
|
|
SC_PKCS15_AODF, NULL);
|
|
}
|
|
|
|
if (r >= 0) {
|
|
r = sc_pkcs15init_update_dir(p15spec, profile, app);
|
|
if (r >= 0)
|
|
r = sc_pkcs15init_update_tokeninfo(p15spec, profile);
|
|
/* FIXME: what to do if sc_pkcs15init_update_dir failed? */
|
|
} else {
|
|
free(app); /* unused */
|
|
}
|
|
|
|
sc_pkcs15init_write_info(card, profile, pin_obj);
|
|
SC_FUNC_RETURN(ctx, 3, r);
|
|
}
|
|
|
|
#if 0
|
|
/* Read the EF(UnusedSpace) file */
|
|
static int sc_pkcs15init_read_unusedspace(sc_pkcs15_card_t *p15card)
|
|
{
|
|
sc_path_t path;
|
|
u8 *buf = NULL;
|
|
size_t buf_len;
|
|
int r;
|
|
|
|
/* Check if EF(UnusedSpace) file is already read */
|
|
if (p15card->unusedspace_read)
|
|
return 0;
|
|
|
|
if (p15card->file_unusedspace != NULL)
|
|
path = p15card->file_unusedspace->path;
|
|
else {
|
|
path = p15card->file_app->path;
|
|
sc_append_path_id(&path, (const u8 *) "\x50\x33", 2);
|
|
path.count = -1;
|
|
}
|
|
|
|
r = sc_pkcs15_read_file(p15card, &path, &buf, &buf_len, NULL);
|
|
if (r < 0) {
|
|
if (r == SC_ERROR_FILE_NOT_FOUND)
|
|
r = 0;
|
|
goto err;
|
|
}
|
|
|
|
r = sc_pkcs15_parse_unusedspace(buf, buf_len, p15card);
|
|
|
|
err:
|
|
if (buf != NULL)
|
|
free(buf);
|
|
return r;
|
|
}
|
|
|
|
/* Update the EF(UnusedSpace) file */
|
|
static int sc_pkcs15init_update_unusedspace(sc_pkcs15_card_t *p15card,
|
|
sc_profile_t *profile)
|
|
{
|
|
u8 *buf = NULL;
|
|
size_t buf_len;
|
|
sc_file_t *file = NULL;
|
|
int r;
|
|
|
|
/* Make sure we've read the EF(UnusedSpace) file first */
|
|
r = sc_pkcs15init_read_unusedspace(p15card);
|
|
if (r < 0)
|
|
return r;
|
|
if (p15card->unusedspace_list == NULL)
|
|
return 0;
|
|
|
|
r = sc_profile_get_file(profile, "PKCS15-UnusedSpace", &file);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sc_pkcs15_encode_unusedspace(p15card->card->ctx, p15card, &buf, &buf_len);
|
|
if (r < 0)
|
|
goto err;
|
|
|
|
r = sc_pkcs15init_update_file(profile, p15card->card,
|
|
file, buf, buf_len);
|
|
|
|
err:
|
|
if (buf != NULL)
|
|
free(buf);
|
|
if (file != NULL)
|
|
sc_file_free(file);
|
|
return r;
|
|
}
|
|
|
|
/* Called by sc_pkcs15init_add_unusedspace(), to try to merge path
|
|
* with one of the paths in us, so one large path can be made */
|
|
static sc_pkcs15_unusedspace_t *merge_paths(sc_pkcs15_unusedspace_t *us,
|
|
const sc_path_t *path)
|
|
{
|
|
for ( ; us != NULL; us = us->next) {
|
|
sc_path_t *old = &us->path;
|
|
if (!sc_compare_path(path, old))
|
|
continue;
|
|
if (old->index + old->count == path->index) {
|
|
old->count += path->count;
|
|
return us;
|
|
}
|
|
if (path->index + path->count == old->index) {
|
|
old->index = path->index;
|
|
old->count += path->count;
|
|
return us;
|
|
}
|
|
}
|
|
return NULL; /* Couldn't merge */
|
|
}
|
|
|
|
/* Add a path to the EF(UnusedSpace) file. This is done when (part of) the
|
|
* file where the path points to is no longer used (i.e. the pkcs15 object
|
|
* inside has been "deleted"). */
|
|
static int sc_pkcs15init_add_unusedspace(sc_pkcs15_card_t *p15card,
|
|
sc_profile_t *profile, const sc_path_t *path, const sc_pkcs15_id_t *auth_id)
|
|
{
|
|
sc_pkcs15_unusedspace_t *us;
|
|
int r = 0;
|
|
|
|
if (path->count == -1)
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
/* Make sure we've read the EF(UnusedSpace) file first */
|
|
r = sc_pkcs15init_read_unusedspace(p15card);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
/* See if we can merge this new entry with one that already exists */
|
|
us = merge_paths(p15card->unusedspace_list, path);
|
|
if (us == NULL)
|
|
sc_pkcs15_add_unusedspace(p15card, path, auth_id);
|
|
else {
|
|
/* So we could merge it. But now the path pointed to by us
|
|
* might be mergeable with another path further on in the list */
|
|
if (merge_paths(us->next, &us->path))
|
|
sc_pkcs15_remove_unusedspace(p15card, us);
|
|
}
|
|
|
|
return sc_pkcs15init_update_unusedspace(p15card, profile);
|
|
}
|
|
|
|
/* Remove some space from the EF(UnusedSpace) file. This is done when you want
|
|
* to use the space for a certificate, data, ... */
|
|
static int sc_pkcs15init_remove_unusedspace(sc_pkcs15_card_t *p15card,
|
|
sc_profile_t *profile, const sc_path_t *path)
|
|
{
|
|
sc_pkcs15_unusedspace_t *us;
|
|
int ok = 0;
|
|
int r = 0;
|
|
|
|
/* Make sure we've read the EF(UnusedSpace) file first */
|
|
r = sc_pkcs15init_read_unusedspace(p15card);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
/* Search in the EF(UnusedSpace) for a path where the required
|
|
* space (referred to by 'path') can be subtracted from */
|
|
for (us = p15card->unusedspace_list; us != NULL && !ok; us = us->next) {
|
|
sc_path_t *old = &us->path;
|
|
if (!sc_compare_path(path, old) || old->count < path->count)
|
|
continue;
|
|
if (old->index == path->index) {
|
|
old->index += path->count;
|
|
old->count -= path->count;
|
|
ok = 1;
|
|
}
|
|
else if (old->index + old->count == path->index + path->count) {
|
|
old->count -= path->count;
|
|
ok = 1;
|
|
}
|
|
if (old->count == 0)
|
|
sc_pkcs15_remove_unusedspace(p15card, us);
|
|
}
|
|
|
|
if (!ok)
|
|
return SC_ERROR_OBJECT_NOT_FOUND; /* the space couldn't be found */
|
|
|
|
return sc_pkcs15init_update_unusedspace(p15card, profile);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Store a PIN/PUK pair
|
|
*/
|
|
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_pin_info *pin_info;
|
|
int r, idx;
|
|
char puk_label[0x30];
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
if (!args->puk_id.len)
|
|
SC_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)
|
|
SC_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)
|
|
SC_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate PIN object");
|
|
|
|
pin_info = (sc_pkcs15_pin_info_t *) pin_obj->data;
|
|
|
|
sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, pin_info);
|
|
pin_info->auth_id = args->puk_id;
|
|
|
|
/* Set the SO PIN reference from card */
|
|
r = set_so_pin_from_card(p15card, profile);
|
|
if (r < 0)
|
|
sc_pkcs15_free_object(pin_obj);
|
|
SC_TEST_RET(ctx, r, "Failed to set SO PIN reference from card");
|
|
|
|
/* Now store the PINs */
|
|
if (profile->ops->create_pin)
|
|
r = sc_pkcs15init_create_pin(p15card, profile, pin_obj, args);
|
|
else
|
|
SC_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "In Old API store PUK object is not supported");
|
|
|
|
/* Fix up any ACLs referring to the user pin */
|
|
if (r >= 0)
|
|
sc_keycache_set_pin_name(&pin_info->path, pin_info->reference,
|
|
SC_PKCS15INIT_USER_PUK);
|
|
|
|
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;
|
|
|
|
SC_FUNC_RETURN(ctx, 3, 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_pin_info *pin_info;
|
|
int r, idx;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
/* 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)
|
|
SC_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)
|
|
SC_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)
|
|
SC_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate PIN object");
|
|
|
|
pin_info = (sc_pkcs15_pin_info_t *) pin_obj->data;
|
|
|
|
sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, pin_info);
|
|
pin_info->auth_id = args->auth_id;
|
|
|
|
/* Set the SO PIN reference from card */
|
|
r = set_so_pin_from_card(p15card, profile);
|
|
if (r < 0)
|
|
sc_pkcs15_free_object(pin_obj);
|
|
SC_TEST_RET(ctx, r, "Failed to set SO PIN reference from card");
|
|
|
|
/* Now store the PINs */
|
|
if (profile->ops->create_pin) {
|
|
r = sc_pkcs15init_create_pin(p15card, profile, pin_obj, args);
|
|
} else {
|
|
/* Get the number of PINs we already have */
|
|
idx = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH, NULL, 0);
|
|
|
|
r = profile->ops->new_pin(profile, p15card->card, pin_info, idx,
|
|
args->pin, args->pin_len,
|
|
args->puk, args->puk_len);
|
|
}
|
|
|
|
if (r < 0)
|
|
sc_pkcs15_free_object(pin_obj);
|
|
SC_TEST_RET(ctx, r, "Card specific create PIN failed.");
|
|
|
|
/* Fix up any ACLs referring to the user pin */
|
|
sc_keycache_set_pin_name(&pin_info->path, pin_info->reference, SC_PKCS15INIT_USER_PIN);
|
|
|
|
r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_AODF, pin_obj);
|
|
if (r < 0)
|
|
sc_pkcs15_free_object(pin_obj);
|
|
SC_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;
|
|
|
|
SC_FUNC_RETURN(ctx, 3, r);
|
|
}
|
|
|
|
|
|
static int
|
|
sc_pkcs15init_create_pin(sc_pkcs15_card_t *p15card, sc_profile_t *profile,
|
|
sc_pkcs15_object_t *pin_obj,
|
|
struct sc_pkcs15init_pinargs *args)
|
|
{
|
|
struct sc_context *ctx = p15card->card->ctx;
|
|
sc_pkcs15_pin_info_t *pin_info = (sc_pkcs15_pin_info_t *) pin_obj->data;
|
|
sc_card_t *card = p15card->card;
|
|
sc_file_t *df = profile->df_info->file;
|
|
int r, retry = 0;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
/* 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)
|
|
SC_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PIN domains not supported.");
|
|
|
|
r = profile->ops->create_domain(profile, card, &pin_info->auth_id, &df);
|
|
SC_TEST_RET(ctx, r, "Card specific create domain failed");
|
|
}
|
|
|
|
/* Path encoded only for local PINs */
|
|
if (pin_info->flags & SC_PKCS15_PIN_FLAG_LOCAL)
|
|
pin_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, card, pin_info);
|
|
SC_TEST_RET(ctx, r, "Card specific select PIN reference failed");
|
|
|
|
retry = 1;
|
|
}
|
|
|
|
r = sc_pkcs15_find_pin_by_reference(p15card, &pin_info->path,
|
|
pin_info->reference, NULL);
|
|
if (r == SC_ERROR_OBJECT_NOT_FOUND)
|
|
break;
|
|
|
|
if (r != 0 || !retry)
|
|
/* Other error trying to retrieve pin obj */
|
|
SC_TEST_RET(ctx, SC_ERROR_TOO_MANY_OBJECTS, "Failed to allocate PIN reference.");
|
|
|
|
pin_info->reference++;
|
|
}
|
|
|
|
sc_keycache_set_pin_name(&pin_info->path, pin_info->reference,
|
|
SC_PKCS15INIT_USER_PIN);
|
|
|
|
if (args->puk_len == 0)
|
|
pin_info->flags |= SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED;
|
|
|
|
sc_debug(ctx, "create PIN with reference:%X, flags:%X, path:%s",
|
|
pin_info->reference, pin_info->flags, sc_print_path(&pin_info->path));
|
|
r = profile->ops->create_pin(profile, card,
|
|
df, pin_obj,
|
|
args->pin, args->pin_len,
|
|
args->puk, args->puk_len);
|
|
|
|
if (df != profile->df_info->file)
|
|
sc_file_free(df);
|
|
|
|
SC_FUNC_RETURN(ctx, 3, r);
|
|
}
|
|
|
|
/*
|
|
* Default function for creating a pin subdirectory
|
|
*/
|
|
int
|
|
sc_pkcs15_create_pin_domain(sc_profile_t *profile, sc_card_t *card,
|
|
const sc_pkcs15_id_t *id, sc_file_t **ret)
|
|
{
|
|
sc_file_t *df = profile->df_info->file;
|
|
int r;
|
|
|
|
/* 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)
|
|
r = profile->ops->create_dir(profile, card, *ret);
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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;
|
|
struct sc_card *card = p15card->card;
|
|
const char *label;
|
|
unsigned int usage;
|
|
int r = 0;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
if (!res_obj || !keybits)
|
|
SC_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 */
|
|
object = sc_pkcs15init_new_object(prkey_pkcs15_algo(p15card, key),
|
|
label, &keyargs->auth_id, NULL);
|
|
if (object == NULL)
|
|
SC_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate new PrKey object");
|
|
|
|
key_info = (sc_pkcs15_prkey_info_t *) object->data;
|
|
key_info->usage = usage;
|
|
key_info->native = 1;
|
|
key_info->key_reference = 0;
|
|
key_info->modulus_length = keybits;
|
|
key_info->access_flags = DEFAULT_PRKEY_ACCESS_FLAGS;
|
|
/* Path is selected below */
|
|
|
|
if (keyargs->flags & SC_PKCS15INIT_EXTRACTABLE) {
|
|
key_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE;
|
|
key_info->access_flags &= ~SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE;
|
|
key_info->native = 0;
|
|
}
|
|
|
|
if (keyargs->id.len != 0 && (keyargs->flags & SC_PKCS15INIT_SPLIT_KEY)) {
|
|
/* Split key; this ID exists already, don't check for
|
|
* the pkcs15 object */
|
|
} else {
|
|
/* 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);
|
|
SC_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 = malloc(key_info->params_len);
|
|
if (!key_info->params)
|
|
SC_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate memory for GOST parameters");
|
|
keyinfo_gostparams = key_info->params;
|
|
keyinfo_gostparams->gostr3410 = keyargs->gost_params.gostr3410;
|
|
keyinfo_gostparams->gostr3411 = keyargs->gost_params.gostr3411;
|
|
keyinfo_gostparams->gost28147 = keyargs->gost_params.gost28147;
|
|
}
|
|
|
|
r = select_object_path(p15card, profile, object, &key_info->id, &key_info->path);
|
|
SC_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_pkcs15_object_t *dummy;
|
|
|
|
r = profile->ops->select_key_reference(profile, card, key_info);
|
|
SC_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, &dummy);
|
|
if (r == SC_ERROR_OBJECT_NOT_FOUND)
|
|
break;
|
|
|
|
if (r != 0)
|
|
/* Other error trying to retrieve pin obj */
|
|
SC_TEST_RET(ctx, SC_ERROR_TOO_MANY_OBJECTS, "Failed to select key reference");
|
|
|
|
key_info->key_reference++;
|
|
}
|
|
}
|
|
|
|
*res_obj = object;
|
|
|
|
SC_FUNC_RETURN(ctx, 3, 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;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
/* check supported key size */
|
|
r = check_key_size(p15card->card, keygen_args->prkey_args.key.algorithm, keybits);
|
|
SC_TEST_RET(ctx, r, "Invalid key size");
|
|
|
|
/* For now, we support just RSA and GOST key pair generation */
|
|
if (!check_key_compatibility(p15card, &keygen_args->prkey_args.key,
|
|
keygen_args->prkey_args.x509_usage,
|
|
keybits, SC_ALGORITHM_ONBOARD_KEY_GEN))
|
|
SC_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Generation of RSA and GOST keys is only supported");
|
|
|
|
if (profile->ops->generate_key == NULL && profile->ops->old_generate_key == NULL)
|
|
SC_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Key generation not supported");
|
|
|
|
/* Set the USER PIN reference from args */
|
|
r = set_user_pin_from_authid(p15card, profile, &keygen_args->prkey_args.auth_id);
|
|
SC_TEST_RET(ctx, r, "Cannot set User PIN from auth. ID");
|
|
|
|
/* Set the SO PIN reference from card */
|
|
r = set_so_pin_from_card(p15card, profile);
|
|
SC_TEST_RET(ctx, r, "Cannot set SO PIN from card");
|
|
|
|
caller_supplied_id = keygen_args->prkey_args.id.len != 0;
|
|
|
|
/* Set up the PrKDF object */
|
|
r = sc_pkcs15init_init_prkdf(p15card, profile, &keygen_args->prkey_args,
|
|
&keygen_args->prkey_args.key, keybits, &object);
|
|
SC_TEST_RET(ctx, r, "Set up private key object error");
|
|
|
|
key_info = (struct sc_pkcs15_prkey_info *) object->data;
|
|
|
|
/* Set up the PuKDF info. The public key will be filled in
|
|
* by the card driver's generate_key function called below */
|
|
memset(&pubkey_args, 0, sizeof(pubkey_args));
|
|
pubkey_args.id = keygen_args->prkey_args.id;
|
|
#if 0
|
|
pubkey_args.auth_id = keygen_args->prkey_args.auth_id;
|
|
#endif
|
|
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.gost_params = keygen_args->prkey_args.gost_params;
|
|
|
|
/* Generate the private key on card */
|
|
if (profile->ops->create_key) {
|
|
/* New API */
|
|
r = profile->ops->create_key(profile, p15card->card, object);
|
|
SC_TEST_RET(ctx, r, "Cannot generate key: create key failed");
|
|
|
|
r = profile->ops->generate_key(profile, p15card->card, object, &pubkey_args.key);
|
|
SC_TEST_RET(ctx, r, "Failed to generate key");
|
|
} else {
|
|
int idx;
|
|
|
|
idx = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, NULL, 0);
|
|
r = profile->ops->old_generate_key(profile, p15card->card, idx, keybits,
|
|
&pubkey_args.key, key_info);
|
|
SC_TEST_RET(ctx, r, "Failed to generate key in an old manner");
|
|
}
|
|
|
|
/* 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);
|
|
SC_TEST_RET(ctx, r, "Select intrinsic ID error");
|
|
|
|
if (iid.len) {
|
|
key_info->id = iid;
|
|
pubkey_args.id = iid;
|
|
}
|
|
}
|
|
|
|
r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_PRKDF, object);
|
|
SC_TEST_RET(ctx, r, "Failed to add generated private key object");
|
|
|
|
r = sc_pkcs15init_store_public_key(p15card, profile, &pubkey_args, NULL);
|
|
SC_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;
|
|
|
|
SC_FUNC_RETURN(ctx, 3, r);
|
|
}
|
|
|
|
|
|
/*
|
|
* Store private key
|
|
*/
|
|
int
|
|
sc_pkcs15init_store_private_key(struct sc_pkcs15_card *p15card,
|
|
struct sc_profile *profile,
|
|
struct sc_pkcs15init_prkeyargs *keyargs,
|
|
struct sc_pkcs15_object **res_obj)
|
|
{
|
|
struct sc_context *ctx = p15card->card->ctx;
|
|
struct sc_pkcs15_object *object;
|
|
struct sc_pkcs15_prkey_info *key_info;
|
|
struct sc_pkcs15_prkey key;
|
|
int keybits, idx, r = 0;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
/* Create a copy of the key first */
|
|
key = keyargs->key;
|
|
|
|
r = prkey_fixup(p15card, &key);
|
|
SC_TEST_RET(ctx, r, "Private key data sanity check failed");
|
|
|
|
keybits = prkey_bits(p15card, &key);
|
|
SC_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 non-natively. */
|
|
if (!(keyargs->flags & SC_PKCS15INIT_EXTRACTABLE))
|
|
SC_TEST_RET(ctx, SC_ERROR_INCOMPATIBLE_KEY, "Card does not support this key.");
|
|
|
|
if (!keyargs->passphrase
|
|
&& !(keyargs->flags & SC_PKCS15INIT_NO_PASSPHRASE))
|
|
SC_TEST_RET(ctx, SC_ERROR_PASSPHRASE_REQUIRED, "No key encryption passphrase given.");
|
|
}
|
|
|
|
/* Set the USER PIN reference from args */
|
|
r = set_user_pin_from_authid(p15card, profile, &keyargs->auth_id);
|
|
SC_TEST_RET(ctx, r, "Failed to get the associated user PIN");
|
|
|
|
/* Set the SO PIN reference from card */
|
|
r = set_so_pin_from_card(p15card, profile);
|
|
SC_TEST_RET(ctx, r, "Failed to get SO PIN from card");
|
|
|
|
/* 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);
|
|
SC_TEST_RET(ctx, r, "Get intrinsic ID error");
|
|
|
|
/* Set up the PrKDF object */
|
|
r = sc_pkcs15init_init_prkdf(p15card, profile, keyargs, &key, keybits, &object);
|
|
SC_TEST_RET(ctx, r, "Failed to initialize private key object");
|
|
key_info = (struct sc_pkcs15_prkey_info *) object->data;
|
|
|
|
/* Get the number of private keys already on this card */
|
|
idx = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_PRKEY, NULL, 0);
|
|
if (!(keyargs->flags & SC_PKCS15INIT_EXTRACTABLE)) {
|
|
if (profile->ops->create_key) {
|
|
/* New API */
|
|
r = profile->ops->create_key(profile, p15card->card, object);
|
|
SC_TEST_RET(ctx, r, "Card specific 'create key' failed");
|
|
|
|
r = profile->ops->store_key(profile, p15card->card, object, &key);
|
|
SC_TEST_RET(ctx, r, "Card specific 'store key' failed");
|
|
} else {
|
|
r = profile->ops->new_key(profile, p15card->card, &key, idx, key_info);
|
|
SC_TEST_RET(ctx, r, "Card specific 'new key' failed");
|
|
}
|
|
} else {
|
|
sc_pkcs15_der_t encoded, wrapped, *der = &encoded;
|
|
sc_context_t *ctx = p15card->card->ctx;
|
|
|
|
/* DER encode the private key */
|
|
encoded.value = wrapped.value = NULL;
|
|
r = sc_pkcs15_encode_prkey(ctx, &key, &encoded.value, &encoded.len);
|
|
SC_TEST_RET(ctx, r, "Failed to encode private key");
|
|
|
|
if (keyargs->passphrase) {
|
|
r = sc_pkcs15_wrap_data(ctx, keyargs->passphrase,
|
|
der->value, der->len,
|
|
&wrapped.value, &wrapped.len);
|
|
if (r < 0) {
|
|
free(der->value);
|
|
SC_TEST_RET(ctx, r, "Failed to wrap private key data");
|
|
}
|
|
der = &wrapped;
|
|
}
|
|
|
|
r = sc_pkcs15init_store_data(p15card, profile,
|
|
object, &keyargs->id, der, &key_info->path);
|
|
|
|
/* If the key is encrypted, flag the PrKDF entry as
|
|
* indirect-protected */
|
|
if (keyargs->passphrase)
|
|
key_info->path.type = SC_PATH_TYPE_PATH_PROT;
|
|
|
|
free(encoded.value);
|
|
free(wrapped.value);
|
|
|
|
SC_TEST_RET(ctx, r, "Failed to store private key data");
|
|
}
|
|
|
|
/* Now update the PrKDF */
|
|
r = sc_pkcs15init_add_object(p15card, profile,
|
|
SC_PKCS15_PRKDF, object);
|
|
|
|
if (r >= 0 && res_obj)
|
|
*res_obj = object;
|
|
|
|
profile->dirty = 1;
|
|
|
|
SC_FUNC_RETURN(ctx, 3, r);
|
|
}
|
|
|
|
|
|
int
|
|
sc_pkcs15init_store_split_key(struct sc_pkcs15_card *p15card,
|
|
struct sc_profile *profile,
|
|
struct sc_pkcs15init_prkeyargs *keyargs,
|
|
struct sc_pkcs15_object **prk1_obj,
|
|
struct sc_pkcs15_object **prk2_obj)
|
|
{
|
|
struct sc_context *ctx = p15card->card->ctx;
|
|
unsigned int usage = keyargs->x509_usage;
|
|
int r;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
/* keyEncipherment|dataEncipherment|keyAgreement */
|
|
keyargs->x509_usage = usage & (SC_PKCS15INIT_X509_KEY_ENCIPHERMENT |
|
|
SC_PKCS15INIT_X509_DATA_ENCIPHERMENT |
|
|
SC_PKCS15INIT_X509_KEY_AGREEMENT);
|
|
r = sc_pkcs15init_store_private_key(p15card, profile,
|
|
keyargs, prk1_obj);
|
|
|
|
if (r >= 0) {
|
|
/* digitalSignature|nonRepudiation|certSign|cRLSign */
|
|
keyargs->x509_usage = usage & (SC_PKCS15INIT_X509_DIGITAL_SIGNATURE |
|
|
SC_PKCS15INIT_X509_NON_REPUDIATION |
|
|
SC_PKCS15INIT_X509_KEY_CERT_SIGN |
|
|
SC_PKCS15INIT_X509_CRL_SIGN);
|
|
|
|
/* Prevent pkcs15init from choking on duplicate ID */
|
|
keyargs->flags |= SC_PKCS15INIT_SPLIT_KEY;
|
|
r = sc_pkcs15init_store_private_key(p15card, profile,
|
|
keyargs, prk2_obj);
|
|
}
|
|
|
|
keyargs->x509_usage = usage;
|
|
SC_FUNC_RETURN(ctx, 3, r);
|
|
}
|
|
|
|
/*
|
|
* Store a public key
|
|
*
|
|
* TODO: set public key reference
|
|
*/
|
|
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_pkcs15_der der_encoded;
|
|
struct sc_path *path;
|
|
const char *label;
|
|
unsigned int keybits, type, usage;
|
|
int r;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
if (!keyargs)
|
|
SC_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;
|
|
default:
|
|
SC_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported key algorithm.");
|
|
}
|
|
|
|
if ((usage = keyargs->usage) == 0) {
|
|
usage = SC_PKCS15_PRKEY_USAGE_SIGN;
|
|
if (keyargs->x509_usage)
|
|
usage = sc_pkcs15init_map_usage(keyargs->x509_usage, 0);
|
|
}
|
|
if ((label = keyargs->label) == NULL)
|
|
label = "Public Key";
|
|
|
|
/* Set up the pkcs15 object. */
|
|
object = sc_pkcs15init_new_object(type, label, &keyargs->auth_id, NULL);
|
|
if (object == NULL)
|
|
SC_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate new public key object");
|
|
|
|
key_info = (sc_pkcs15_pubkey_info_t *) 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 = malloc(key_info->params_len);
|
|
if (!key_info->params)
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
keyinfo_gostparams = key_info->params;
|
|
keyinfo_gostparams->gostr3410 = keyargs->gost_params.gostr3410;
|
|
keyinfo_gostparams->gostr3411 = keyargs->gost_params.gostr3411;
|
|
keyinfo_gostparams->gost28147 = keyargs->gost_params.gost28147;
|
|
}
|
|
|
|
/* 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);
|
|
SC_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);
|
|
SC_TEST_RET(ctx, r, "Failed to select public key object ID");
|
|
|
|
key_info->id = keyargs->id;
|
|
|
|
/* DER encode public key components */
|
|
r = sc_pkcs15_encode_pubkey(p15card->card->ctx, &key,
|
|
&der_encoded.value, &der_encoded.len);
|
|
SC_TEST_RET(ctx, r, "Encode public key error");
|
|
|
|
/* Now create key file and store key */
|
|
r = sc_pkcs15init_store_data(p15card, profile,
|
|
object, &keyargs->id,
|
|
&der_encoded, &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;
|
|
|
|
if (der_encoded.value)
|
|
free(der_encoded.value);
|
|
|
|
profile->dirty = 1;
|
|
|
|
SC_FUNC_RETURN(ctx, 3, 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;
|
|
struct sc_pkcs15_object *object;
|
|
unsigned int usage;
|
|
const char *label;
|
|
int r;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
usage = SC_PKCS15_PRKEY_USAGE_SIGN;
|
|
if (args->x509_usage)
|
|
usage = sc_pkcs15init_map_usage(args->x509_usage, 0);
|
|
if ((label = args->label) == NULL)
|
|
label = "Certificate";
|
|
|
|
/* Set the SO PIN reference from card */
|
|
r = set_so_pin_from_card(p15card, profile);
|
|
SC_TEST_RET(ctx, r, "Set SO PIN from card error");
|
|
|
|
r = select_intrinsic_id(p15card, profile, SC_PKCS15_TYPE_CERT_X509, &args->id, &args->der_encoded);
|
|
SC_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);
|
|
SC_TEST_RET(ctx, r, "Select certificate ID error");
|
|
|
|
if (profile->protect_certificates) {
|
|
/* If there is a private key corresponding to the ID given
|
|
* by the user, make sure $PIN references the pin protecting
|
|
* this key
|
|
*/
|
|
r = -1;
|
|
if (args->id.len != 0
|
|
&& sc_pkcs15_find_prkey_by_id(p15card, &args->id, &object) == 0) {
|
|
r = set_user_pin_from_authid(p15card, profile, &object->auth_id);
|
|
SC_TEST_RET(ctx, r, "Failed to assign user pin reference "
|
|
"(copied from private key auth_id)");
|
|
}
|
|
if (r == -1) /* User pin ref not yet set */
|
|
set_user_pin_from_authid(p15card, profile, NULL);
|
|
}
|
|
|
|
object = sc_pkcs15init_new_object(SC_PKCS15_TYPE_CERT_X509, label, NULL, NULL);
|
|
if (object == NULL)
|
|
SC_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Failed to allocate certificate object");
|
|
cert_info = (sc_pkcs15_cert_info_t *) object->data;
|
|
cert_info->id = args->id;
|
|
cert_info->authority = args->authority;
|
|
|
|
if (profile->pkcs15.direct_certificates) {
|
|
sc_der_copy(&cert_info->value, &args->der_encoded);
|
|
} else {
|
|
r = sc_pkcs15init_store_data(p15card, profile,
|
|
object, &args->id,
|
|
&args->der_encoded, &cert_info->path);
|
|
}
|
|
|
|
/* Remove the corresponding public key object, if it exists. */
|
|
if (r >= 0 && !profile->keep_public_key) {
|
|
sc_pkcs15_object_t *puk = NULL;
|
|
|
|
r = sc_pkcs15_find_pubkey_by_id(p15card, &cert_info->id, &puk);
|
|
if (r == 0)
|
|
r = sc_pkcs15init_remove_object(p15card, profile, puk);
|
|
else if (r == SC_ERROR_OBJECT_NOT_FOUND)
|
|
r = 0;
|
|
}
|
|
|
|
/* Now update the CDF */
|
|
if (r >= 0) {
|
|
r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_CDF, object);
|
|
} else
|
|
sc_pkcs15_free_object(object);
|
|
|
|
if (r >= 0 && res_obj)
|
|
*res_obj = object;
|
|
|
|
profile->dirty = 1;
|
|
|
|
SC_FUNC_RETURN(ctx, 3, 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_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;
|
|
|
|
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);
|
|
if (r < 0)
|
|
return r;
|
|
for (i = 0; i < r; i++) {
|
|
u8 cid;
|
|
struct sc_pkcs15_data_info *cinfo;
|
|
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;
|
|
}
|
|
|
|
/* Set the USER PIN reference from args */
|
|
r = set_user_pin_from_authid(p15card, profile, &args->auth_id);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
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 = (sc_pkcs15_data_info_t *) 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;
|
|
|
|
r = sc_pkcs15init_store_data(p15card, profile,
|
|
object, &args->id, &args->der_encoded,
|
|
&data_object_info->path);
|
|
|
|
/* Now update the DDF */
|
|
if (r >= 0)
|
|
r = sc_pkcs15init_add_object(p15card, profile,
|
|
SC_PKCS15_DODF, object);
|
|
|
|
if (r >= 0 && res_obj)
|
|
*res_obj = object;
|
|
|
|
profile->dirty = 1;
|
|
|
|
return r;
|
|
}
|
|
|
|
static int
|
|
sc_pkcs15init_store_data(struct sc_pkcs15_card *p15card,
|
|
struct sc_profile *profile,
|
|
struct sc_pkcs15_object *object,
|
|
struct sc_pkcs15_id *id,
|
|
struct sc_pkcs15_der *data,
|
|
struct sc_path *path)
|
|
{
|
|
struct sc_context *ctx = p15card->card->ctx;
|
|
struct sc_file *file = NULL;
|
|
int r;
|
|
unsigned int idx = -1;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
/* Set the SO PIN reference from card */
|
|
r = set_so_pin_from_card(p15card, profile);
|
|
SC_TEST_RET(ctx, r, "Failed to set SO PIN from card");
|
|
|
|
if (profile->ops->new_file == NULL) {
|
|
/* New API */
|
|
r = select_object_path(p15card, profile, object, id, path);
|
|
SC_TEST_RET(ctx, r, "Failed to select object path");
|
|
|
|
r = sc_profile_get_file_by_path(profile, path, &file);
|
|
SC_TEST_RET(ctx, r, "Failed to get file by path");
|
|
} else {
|
|
/* Get the number of objects of this type already on this card */
|
|
idx = sc_pkcs15_get_objects(p15card,
|
|
object->type & SC_PKCS15_TYPE_CLASS_MASK,
|
|
NULL, 0);
|
|
|
|
/* Allocate data file */
|
|
r = profile->ops->new_file(profile, p15card->card,
|
|
object->type, idx, &file);
|
|
if (r < 0) {
|
|
sc_debug(p15card->card->ctx, "Unable to allocate file");
|
|
goto done;
|
|
}
|
|
}
|
|
if (file->path.count == 0) {
|
|
file->path.index = 0;
|
|
file->path.count = -1;
|
|
}
|
|
r = sc_pkcs15init_update_file(profile, p15card->card,
|
|
file, data->value, data->len);
|
|
|
|
*path = file->path;
|
|
|
|
done: if (file)
|
|
sc_file_free(file);
|
|
SC_FUNC_RETURN(ctx, 3, 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(sc_pkcs15_bignum_t *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 if the key size is supported.
|
|
*/
|
|
static int
|
|
check_key_size(sc_card_t *card, unsigned int alg,
|
|
unsigned int bits)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < card->algorithm_count; i++) {
|
|
sc_algorithm_info_t *info = &card->algorithms[i];
|
|
|
|
if (info->algorithm != alg)
|
|
continue;
|
|
if (info->key_length != bits)
|
|
continue;
|
|
return SC_SUCCESS;
|
|
}
|
|
return 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)
|
|
{
|
|
sc_algorithm_info_t *info;
|
|
unsigned int count;
|
|
int bad_usage = 0;
|
|
|
|
count = p15card->card->algorithm_count;
|
|
for (info = p15card->card->algorithms; count--; info++) {
|
|
/* XXX: check for equality, or <= ? */
|
|
if (info->algorithm != key->algorithm
|
|
|| info->key_length != key_length
|
|
|| (info->flags & flags) != flags)
|
|
continue;
|
|
if (key->algorithm == SC_ALGORITHM_RSA
|
|
&& info->u._rsa.exponent != 0
|
|
&& key->u.rsa.exponent.len != 0) {
|
|
sc_pkcs15_bignum_t *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;
|
|
}
|
|
|
|
/* Some cards will not support keys to do
|
|
* both sign/decrypt.
|
|
* For the convenience of the user, catch these
|
|
* here. */
|
|
if (info->flags & SC_ALGORITHM_NEED_USAGE) {
|
|
unsigned int usage;
|
|
|
|
usage = sc_pkcs15init_map_usage(x509_usage, 1);
|
|
if ((usage & (SC_PKCS15_PRKEY_USAGE_UNWRAP
|
|
|SC_PKCS15_PRKEY_USAGE_DECRYPT))
|
|
&& (usage & SC_PKCS15_PRKEY_USAGE_SIGN)) {
|
|
bad_usage = 1;
|
|
continue;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
return bad_usage? -1 : 0;
|
|
}
|
|
|
|
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)
|
|
{
|
|
int res;
|
|
|
|
res = __check_key_compatibility(p15card, key,
|
|
x509_usage, key_length, flags);
|
|
if (res < 0) {
|
|
sc_debug(p15card->card->ctx,
|
|
"This device requires that keys have a "
|
|
"specific key usage.\n"
|
|
"Keys can be used for either signature or decryption, "
|
|
"but not both.\n"
|
|
"Please specify a key usage.\n");
|
|
res = 0;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int
|
|
sc_pkcs15init_requires_restrictive_usage(struct sc_pkcs15_card *p15card,
|
|
struct sc_pkcs15init_prkeyargs *keyargs,
|
|
unsigned int key_length)
|
|
{
|
|
int res;
|
|
|
|
if (key_length == 0)
|
|
key_length = prkey_bits(p15card, &keyargs->key);
|
|
|
|
res = __check_key_compatibility(p15card, &keyargs->key,
|
|
keyargs->x509_usage,
|
|
key_length, 0);
|
|
return res < 0;
|
|
}
|
|
|
|
/*
|
|
* Check RSA key for consistency, and compute missing
|
|
* CRT elements
|
|
*/
|
|
static int
|
|
prkey_fixup_rsa(sc_pkcs15_card_t *p15card, struct sc_pkcs15_prkey_rsa *key)
|
|
{
|
|
if (!key->modulus.len || !key->exponent.len
|
|
|| !key->d.len || !key->p.len || !key->q.len) {
|
|
sc_debug(p15card->card->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_new();
|
|
BN_CTX *ctx = BN_CTX_new();
|
|
|
|
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();
|
|
ctx = BN_CTX_new();
|
|
|
|
BN_sub(aux, rsa->q, BN_value_one());
|
|
BN_mod(rsa->dmq1, rsa->d, aux, ctx);
|
|
|
|
BN_sub(aux, rsa->p, BN_value_one());
|
|
BN_mod(rsa->dmp1, rsa->d, aux, ctx);
|
|
|
|
BN_mod_inverse(rsa->iqmp, rsa->q, rsa->p, ctx);
|
|
|
|
BN_clear_free(aux);
|
|
BN_CTX_free(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(sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_t *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(sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_t *key)
|
|
{
|
|
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_debug(p15card->card->ctx, "Unsupported key (keybits %u)\n",
|
|
sc_pkcs15init_keybits(&key->u.gostr3410.d));
|
|
return SC_ERROR_OBJECT_NOT_VALID;
|
|
}
|
|
return SC_PKCS15_GOSTR3410_KEYSIZE;
|
|
}
|
|
sc_debug(p15card->card->ctx, "Unsupported key algorithm.\n");
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
static int
|
|
prkey_pkcs15_algo(sc_pkcs15_card_t *p15card, sc_pkcs15_prkey_t *key)
|
|
{
|
|
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;
|
|
}
|
|
sc_debug(p15card->card->ctx, "Unsupported key algorithm.\n");
|
|
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(sc_pkcs15_card_t *p15card, struct sc_profile *profile,
|
|
int type, sc_pkcs15_id_t *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;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
#ifndef ENABLE_OPENSSL
|
|
SC_FUNC_RETURN(ctx, 3, SC_SUCCESS);
|
|
#else
|
|
/* ID already exists */
|
|
if (id->len)
|
|
SC_FUNC_RETURN(ctx, 3, SC_SUCCESS);
|
|
|
|
/* Native ID style is not an intrisic one */
|
|
if (profile->id_style == SC_PKCS15INIT_ID_STYLE_NATIVE)
|
|
SC_FUNC_RETURN(ctx, 3, 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);
|
|
SC_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);
|
|
SC_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_debug(ctx, "Intrinsic ID is not implemented for the object type 0x%X\n", type);
|
|
SC_FUNC_RETURN(ctx, 3, 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;
|
|
|
|
/* 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
|
|
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);
|
|
SC_TEST_RET(ctx, rv, "Encoding public key error");
|
|
|
|
if (!id_data || !id_data_len)
|
|
SC_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_debug(ctx, "Unsupported ID style: %i", profile->id_style);
|
|
SC_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Non supported ID style");
|
|
}
|
|
|
|
done:
|
|
if (allocated)
|
|
sc_pkcs15_free_pubkey(pubkey);
|
|
|
|
SC_FUNC_RETURN(ctx, 3, id->len);
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
select_id(struct sc_pkcs15_card *p15card, int type, struct sc_pkcs15_id *id)
|
|
{
|
|
struct sc_pkcs15_id unused_id;
|
|
struct sc_pkcs15_object *obj;
|
|
unsigned int nid = DEFAULT_ID;
|
|
int r;
|
|
|
|
/* 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);
|
|
return (r == SC_ERROR_OBJECT_NOT_FOUND) ? 0 : 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) {
|
|
sc_pkcs15_search_key_t 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;
|
|
return 0;
|
|
}
|
|
|
|
return 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 char *
|
|
get_template_name_from_object (struct sc_context *ctx, sc_pkcs15_object_t *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_context *ctx, sc_pkcs15_object_t *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 = ((sc_pkcs15_prkey_info_t *)obj->data)->path;
|
|
return SC_SUCCESS;
|
|
case SC_PKCS15_TYPE_PUBKEY:
|
|
*ret_path = ((sc_pkcs15_pubkey_info_t *)obj->data)->path;
|
|
return SC_SUCCESS;
|
|
case SC_PKCS15_TYPE_CERT:
|
|
*ret_path = ((sc_pkcs15_cert_info_t *)obj->data)->path;
|
|
return SC_SUCCESS;
|
|
case SC_PKCS15_TYPE_DATA_OBJECT:
|
|
*ret_path = ((sc_pkcs15_data_info_t *)obj->data)->path;
|
|
return SC_SUCCESS;
|
|
case SC_PKCS15_TYPE_AUTH:
|
|
*ret_path = ((sc_pkcs15_pin_info_t *)obj->data)->path;
|
|
return SC_SUCCESS;
|
|
}
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
static int
|
|
select_object_path(sc_pkcs15_card_t *p15card, sc_profile_t *profile,
|
|
sc_pkcs15_object_t *obj, sc_pkcs15_id_t *obj_id,
|
|
sc_path_t *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;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
r = sc_pkcs15_get_objects(p15card, obj->type & SC_PKCS15_TYPE_CLASS_MASK,
|
|
objs, sizeof(objs)/sizeof(objs[0]));
|
|
SC_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);
|
|
SC_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 (ctx, obj);
|
|
if (!name)
|
|
SC_FUNC_RETURN(ctx, 3, SC_SUCCESS);
|
|
|
|
sc_debug(ctx, "key-domain.%s @%s (auth_id.len=%d)\n", 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
|
|
* outside of the 'key-domain' scope. */
|
|
char t_name[0x40];
|
|
|
|
snprintf(t_name, sizeof(t_name), "template-%s", name);
|
|
r = sc_profile_get_file_instance(profile, t_name, indx, &file);
|
|
}
|
|
if (r == SC_ERROR_TEMPLATE_NOT_FOUND)
|
|
SC_FUNC_RETURN(ctx, 3, SC_SUCCESS);
|
|
SC_TEST_RET(ctx, r, "Template instantiation error");
|
|
|
|
if (file->type == SC_FILE_TYPE_BSO)
|
|
break;
|
|
|
|
sc_debug(ctx, "path from instantiated template %s", sc_print_path(&file->path));
|
|
for (ii=0; ii<nn_objs; ii++) {
|
|
r = get_object_path_from_object(ctx, objs[ii], &obj_path);
|
|
SC_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)
|
|
SC_TEST_RET(ctx, SC_ERROR_TOO_MANY_OBJECTS, "Template instantiation error");
|
|
|
|
*path = file->path;
|
|
sc_file_free(file);
|
|
|
|
sc_debug(ctx, "returns object path '%s'", sc_print_path(path));
|
|
SC_FUNC_RETURN(ctx, 3, SC_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* Update EF(DIR)
|
|
*/
|
|
static int
|
|
sc_pkcs15init_update_dir(struct sc_pkcs15_card *p15card,
|
|
struct sc_profile *profile,
|
|
sc_app_info_t *app)
|
|
{
|
|
sc_card_t *card = p15card->card;
|
|
int r, retry = 1;
|
|
|
|
do {
|
|
struct sc_file *dir_file;
|
|
struct sc_path path;
|
|
|
|
r = sc_enum_apps(card);
|
|
|
|
if (r != SC_ERROR_FILE_NOT_FOUND)
|
|
break;
|
|
|
|
sc_format_path("3F002F00", &path);
|
|
if (sc_profile_get_file_by_path(profile, &path, &dir_file) < 0)
|
|
return r;
|
|
r = sc_pkcs15init_update_file(profile, card, 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);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static char *
|
|
get_generalized_time(sc_context_t *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_debug(ctx, "error: gmtime failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
ret = calloc(1, 16);
|
|
if (ret == NULL) {
|
|
sc_debug(ctx, "error: calloc failed\n");
|
|
return NULL;
|
|
}
|
|
/* print time in generalized time format */
|
|
r = strftime(ret, 16, "%Y%m%d%H%M%SZ", tm_time);
|
|
if (r == 0) {
|
|
sc_debug(ctx, "error: strftime failed\n");
|
|
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;
|
|
sc_pkcs15_tokeninfo_t tokeninfo;
|
|
u8 *buf = NULL;
|
|
size_t size;
|
|
int r;
|
|
|
|
/* set lastUpdate field */
|
|
if (p15card->last_update != NULL)
|
|
free(p15card->last_update);
|
|
p15card->last_update = get_generalized_time(card->ctx);
|
|
if (p15card->last_update == NULL)
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
/* create a temporary tokeninfo structure */
|
|
tokeninfo.version = p15card->version;
|
|
/* ugly opensc hack, we use the some high flags internaly */
|
|
tokeninfo.flags = p15card->flags & 0xffffff;
|
|
tokeninfo.label = p15card->label;
|
|
tokeninfo.serial_number = p15card->serial_number;
|
|
tokeninfo.manufacturer_id = p15card->manufacturer_id;
|
|
tokeninfo.last_update = p15card->last_update;
|
|
tokeninfo.preferred_language = p15card->preferred_language;
|
|
|
|
r = sc_pkcs15_encode_tokeninfo(card->ctx, &tokeninfo, &buf, &size);
|
|
if (r >= 0)
|
|
r = sc_pkcs15init_update_file(profile, card,
|
|
p15card->file_tokeninfo, buf, size);
|
|
if (buf)
|
|
free(buf);
|
|
return 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;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
r = sc_pkcs15_encode_odf(ctx, p15card, &buf, &size);
|
|
if (r >= 0)
|
|
r = sc_pkcs15init_update_file(profile, p15card->card,
|
|
p15card->file_odf, buf, size);
|
|
if (buf)
|
|
free(buf);
|
|
SC_FUNC_RETURN(ctx, 3, r);
|
|
}
|
|
|
|
/*
|
|
* Update any PKCS15 DF file (except ODF and DIR)
|
|
*/
|
|
int
|
|
sc_pkcs15init_update_any_df(sc_pkcs15_card_t *p15card,
|
|
sc_profile_t *profile,
|
|
sc_pkcs15_df_t *df,
|
|
int is_new)
|
|
{
|
|
struct sc_card *card = p15card->card;
|
|
sc_file_t *file = df->file, *pfile = NULL;
|
|
u8 *buf = NULL;
|
|
size_t bufsize;
|
|
int update_odf = is_new, r = 0;
|
|
|
|
if (!sc_profile_get_file_by_path(profile, &df->path, &pfile))
|
|
file = pfile;
|
|
|
|
r = sc_pkcs15_encode_df(card->ctx, p15card, df, &buf, &bufsize);
|
|
if (r >= 0) {
|
|
r = sc_pkcs15init_update_file(profile, card,
|
|
file, buf, bufsize);
|
|
|
|
#if 0
|
|
/* If the DF is empty, delete it and remove
|
|
* the corresponding entry from the ODF
|
|
*
|
|
* XXX Before enabling this we should make this a
|
|
* profile option, because not all cards allow
|
|
* arbitrary removal of files.
|
|
*/
|
|
if (bufsize == 0) {
|
|
sc_pkcs15_remove_df(p15card, df);
|
|
sc_file_free(card, df->path);
|
|
update_odf = 1;
|
|
} else
|
|
#endif
|
|
|
|
/* 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 (pfile)
|
|
sc_file_free(pfile);
|
|
|
|
/* Now update the ODF if we have to */
|
|
if (r >= 0 && update_odf)
|
|
r = sc_pkcs15init_update_odf(p15card, profile);
|
|
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Add an object to one of the pkcs15 directory files.
|
|
*/
|
|
static 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;
|
|
struct sc_card *card = p15card->card;
|
|
struct sc_file *file = NULL;
|
|
int is_new = 0, r = 0;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
sc_debug(ctx, "add object %p to DF of type %u\n", object, df_type);
|
|
|
|
df = find_df_by_type(p15card, df_type);
|
|
if (df != NULL) {
|
|
file = df->file;
|
|
} else {
|
|
file = profile->df[df_type];
|
|
if (file == NULL) {
|
|
sc_debug(ctx, "Profile doesn't define a DF file %u",
|
|
df_type);
|
|
SC_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "DF not found in profile");
|
|
}
|
|
sc_pkcs15_add_df(p15card, df_type, &file->path, file);
|
|
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) {
|
|
/* Add nothing; just instantiate this directory file */
|
|
} else if (object->df == NULL) {
|
|
object->df = df;
|
|
r = sc_pkcs15_add_object(p15card, object);
|
|
SC_TEST_RET(ctx, r, "Failed to add pkcs15 object");
|
|
} else {
|
|
/* Reused an existing object */
|
|
assert(object->df == df);
|
|
}
|
|
|
|
r = sc_pkcs15init_update_any_df(p15card, profile, df, is_new);
|
|
SC_FUNC_RETURN(ctx, 3, r);
|
|
#if 0
|
|
if (!sc_profile_get_file_by_path(profile, &df->path, &pfile))
|
|
file = pfile;
|
|
|
|
r = sc_pkcs15_encode_df(card->ctx, p15card, df, &buf, &bufsize);
|
|
if (r >= 0) {
|
|
r = sc_pkcs15init_update_file(profile, card,
|
|
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 (pfile)
|
|
sc_file_free(pfile);
|
|
|
|
/* Now update the ODF if we have to */
|
|
if (r >= 0 && update_odf)
|
|
r = sc_pkcs15init_update_odf(p15card, profile);
|
|
|
|
return r;
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
sc_pkcs15init_remove_object(sc_pkcs15_card_t *p15card,
|
|
sc_profile_t *profile, sc_pkcs15_object_t *obj)
|
|
{
|
|
sc_card_t *card = p15card->card;
|
|
struct sc_pkcs15_df *df;
|
|
sc_path_t path;
|
|
int r = 0;
|
|
|
|
switch(obj->type & SC_PKCS15_TYPE_CLASS_MASK)
|
|
{
|
|
case SC_PKCS15_TYPE_PUBKEY:
|
|
path = ((sc_pkcs15_pubkey_info_t *)obj->data)->path;
|
|
break;
|
|
case SC_PKCS15_TYPE_PRKEY:
|
|
path = ((sc_pkcs15_prkey_info_t *)obj->data)->path;
|
|
break;
|
|
case SC_PKCS15_TYPE_CERT:
|
|
path = ((sc_pkcs15_cert_info_t *)obj->data)->path;
|
|
break;
|
|
case SC_PKCS15_TYPE_DATA_OBJECT:
|
|
path = ((sc_pkcs15_data_info_t *)obj->data)->path;
|
|
break;
|
|
default:
|
|
return SC_ERROR_OBJECT_NOT_FOUND;
|
|
}
|
|
|
|
/* Get the DF we're part of. If there's no DF, fine, we haven't
|
|
* been added yet. */
|
|
if ((df = obj->df) == NULL)
|
|
return 0;
|
|
|
|
/* Unlink the object and update the DF */
|
|
sc_pkcs15_remove_object(p15card, obj);
|
|
if ((r = sc_pkcs15init_update_any_df(p15card, profile, df, 0)) < 0)
|
|
return r;
|
|
|
|
/* XXX Dangerous - the object indicated by path may be the
|
|
* application DF. This isn't true for the Oberthur, but
|
|
* it may be for others. */
|
|
r = sc_delete_file(card, &path);
|
|
|
|
return r;
|
|
}
|
|
|
|
static sc_pkcs15_object_t * sc_pkcs15init_new_object(int type,
|
|
const char *label, sc_pkcs15_id_t *auth_id, void *data)
|
|
{
|
|
sc_pkcs15_object_t *object;
|
|
unsigned int data_size = 0;
|
|
|
|
object = (sc_pkcs15_object_t *) 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(sc_pkcs15_pin_info_t);
|
|
break;
|
|
case SC_PKCS15_TYPE_PRKEY:
|
|
object->flags = DEFAULT_PRKEY_FLAGS;
|
|
data_size = sizeof(sc_pkcs15_prkey_info_t);
|
|
break;
|
|
case SC_PKCS15_TYPE_PUBKEY:
|
|
object->flags = DEFAULT_PUBKEY_FLAGS;
|
|
data_size = sizeof(sc_pkcs15_pubkey_info_t);
|
|
break;
|
|
case SC_PKCS15_TYPE_CERT:
|
|
object->flags = DEFAULT_CERT_FLAGS;
|
|
data_size = sizeof(sc_pkcs15_cert_info_t);
|
|
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(sc_pkcs15_data_info_t);
|
|
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_card *card = p15card->card;
|
|
u8 *buf = NULL;
|
|
size_t bufsize;
|
|
int df_type, r = 0;
|
|
struct sc_pkcs15_df *df;
|
|
|
|
if (object == NULL || object->df == NULL)
|
|
return SC_ERROR_OBJECT_NOT_FOUND;
|
|
df_type = object->df->type;
|
|
|
|
df = find_df_by_type(p15card, df_type);
|
|
if (df == NULL)
|
|
return SC_ERROR_OBJECT_NOT_FOUND;
|
|
|
|
switch(new_attrib_type) {
|
|
case P15_ATTR_TYPE_LABEL:
|
|
if (new_len >= SC_PKCS15_MAX_LABEL_SIZE)
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
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:
|
|
((sc_pkcs15_prkey_info_t *) object->data)->id =
|
|
*((sc_pkcs15_id_t *) new_value);
|
|
break;
|
|
case SC_PKCS15_PUKDF:
|
|
case SC_PKCS15_PUKDF_TRUSTED:
|
|
((sc_pkcs15_pubkey_info_t *) object->data)->id =
|
|
*((sc_pkcs15_id_t *) new_value);
|
|
break;
|
|
case SC_PKCS15_CDF:
|
|
case SC_PKCS15_CDF_TRUSTED:
|
|
case SC_PKCS15_CDF_USEFUL:
|
|
((sc_pkcs15_cert_info_t *) object->data)->id =
|
|
*((sc_pkcs15_id_t *) new_value);
|
|
break;
|
|
default:
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
}
|
|
break;
|
|
default:
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
r = sc_pkcs15_encode_df(card->ctx, p15card, df, &buf, &bufsize);
|
|
if (r >= 0) {
|
|
sc_file_t *file;
|
|
r = sc_profile_get_file_by_path(profile, &df->path, &file);
|
|
if(r<0) return r;
|
|
r = sc_pkcs15init_update_file(profile, card,
|
|
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_file *file = NULL;
|
|
struct sc_path path;
|
|
struct sc_pkcs15_df *df;
|
|
int r, stored_in_ef = 0;
|
|
|
|
switch(obj->type & SC_PKCS15_TYPE_CLASS_MASK) {
|
|
case SC_PKCS15_TYPE_PUBKEY:
|
|
path = ((sc_pkcs15_pubkey_info_t *)obj->data)->path;
|
|
break;
|
|
case SC_PKCS15_TYPE_PRKEY:
|
|
path = ((sc_pkcs15_prkey_info_t *)obj->data)->path;
|
|
break;
|
|
case SC_PKCS15_TYPE_CERT:
|
|
path = ((sc_pkcs15_cert_info_t *)obj->data)->path;
|
|
break;
|
|
case SC_PKCS15_TYPE_DATA_OBJECT:
|
|
path = ((sc_pkcs15_data_info_t *)obj->data)->path;
|
|
break;
|
|
default:
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
sc_debug(p15card->card->ctx, "delete object with path(%X) %s", path.type, sc_print_path(&path));
|
|
r = sc_select_file(p15card->card, &path, &file);
|
|
SC_TEST_RET(p15card->card->ctx, r, "select object path failed");
|
|
|
|
stored_in_ef = (file->type != SC_FILE_TYPE_DF);
|
|
|
|
sc_file_free(file);
|
|
|
|
/* Set the SO PIN reference from card */
|
|
if ((r = set_so_pin_from_card(p15card, profile)) < 0)
|
|
return r;
|
|
|
|
/* If the object is stored in a normal EF, try to delete the EF. */
|
|
if (stored_in_ef) {
|
|
r = sc_pkcs15init_delete_by_path(profile, p15card->card, &path);
|
|
if (r != SC_SUCCESS) {
|
|
sc_debug(p15card->card->ctx, "sc_pkcs15init_delete_by_path failed: %d", r);
|
|
return r;
|
|
}
|
|
}
|
|
else 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->card, obj->type, obj->data, &path);
|
|
if (r < 0) {
|
|
sc_debug(p15card->card->ctx, "ops->delete_object() failed: %d", r);
|
|
return r;
|
|
}
|
|
}
|
|
|
|
/* 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);
|
|
|
|
r = sc_pkcs15init_update_any_df(p15card, profile, df, 0);
|
|
|
|
/* mark card as dirty */
|
|
profile->dirty = 1;
|
|
|
|
return r;
|
|
}
|
|
|
|
int
|
|
sc_pkcs15init_update_certificate(sc_pkcs15_card_t *p15card,
|
|
sc_profile_t *profile,
|
|
sc_pkcs15_object_t *obj,
|
|
const unsigned char *rawcert, size_t certlen)
|
|
{
|
|
sc_file_t *file = NULL, *parent = NULL;
|
|
sc_path_t *path = &((sc_pkcs15_cert_info_t *)obj->data)->path;
|
|
int r;
|
|
|
|
/* Set the SO PIN reference from card */
|
|
if ((r = set_so_pin_from_card(p15card, profile)) < 0)
|
|
return r;
|
|
|
|
r = sc_select_file(p15card->card, path, &file);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
/* If the new cert doesn't fit in the EF, delete it and make the same, but bigger EF */
|
|
if (file->size < certlen) {
|
|
if ((r = sc_pkcs15init_delete_by_path(profile, p15card->card, path)) < 0)
|
|
goto done;
|
|
|
|
file->size = certlen;
|
|
|
|
if ((r = do_select_parent(profile, p15card->card, file, &parent)) < 0
|
|
|| (r = sc_pkcs15init_authenticate(profile, p15card->card,
|
|
parent, SC_AC_OP_CREATE)) < 0)
|
|
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)
|
|
return r;
|
|
if ((r = sc_create_file(p15card->card, file)) < 0)
|
|
goto done;
|
|
}
|
|
|
|
/* Write the new cert */
|
|
if ((r = sc_pkcs15init_authenticate(profile, p15card->card, file, SC_AC_OP_UPDATE)) < 0)
|
|
goto done;
|
|
if ((r = sc_select_file(p15card->card, path, NULL)) < 0)
|
|
goto done;
|
|
if ((r = sc_update_binary(p15card->card, 0, rawcert, certlen, 0)) < 0)
|
|
goto done;
|
|
|
|
/* Fill the remaining space in the EF (if any) with zeros */
|
|
if (certlen < file->size) {
|
|
unsigned char *tmp = (unsigned char *) 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) {
|
|
/* Update the CDF entry */
|
|
path = &((sc_pkcs15_cert_info_t *)obj->data)->path;
|
|
if (file->size != certlen) {
|
|
path->index = 0;
|
|
path->count = certlen;
|
|
}
|
|
else
|
|
path->count = -1;
|
|
r = sc_pkcs15init_update_any_df(p15card, profile, obj->df, 0);
|
|
}
|
|
|
|
/* mark card as dirty */
|
|
profile->dirty = 1;
|
|
|
|
done:
|
|
if (file)
|
|
sc_file_free(file);
|
|
if (parent)
|
|
sc_file_free(parent);
|
|
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* PIN verification
|
|
*/
|
|
static int
|
|
do_get_and_verify_secret(sc_profile_t *pro, sc_card_t *card,
|
|
sc_file_t *file, int type, int reference,
|
|
u8 *pinbuf, size_t *pinsize,
|
|
int verify)
|
|
{
|
|
struct sc_context *ctx = card->ctx;
|
|
struct sc_cardctl_default_key data;
|
|
sc_pkcs15_card_t *p15card = pro->p15_data;
|
|
sc_pkcs15_object_t *pin_obj = NULL;
|
|
sc_pkcs15_pin_info_t pin_info;
|
|
sc_path_t *path;
|
|
const char *ident, *label = NULL;
|
|
int pin_id = -1;
|
|
size_t defsize = 0;
|
|
u8 defbuf[0x100];
|
|
int r;
|
|
int use_pinpad = 0;
|
|
|
|
SC_FUNC_CALLED(ctx, 3);
|
|
path = file? &file->path : NULL;
|
|
|
|
ident = "authentication data";
|
|
if (type == SC_AC_CHV) {
|
|
ident = "PIN";
|
|
memset(&pin_info, 0, sizeof(pin_info));
|
|
pin_info.reference = reference;
|
|
|
|
/* Maybe this is the $SOPIN or $PIN? */
|
|
pin_id = sc_keycache_get_pin_name(path, reference);
|
|
if (pin_id >= 0)
|
|
sc_profile_get_pin_info(pro, pin_id, &pin_info);
|
|
|
|
/* Try to get information on the PIN, such as the
|
|
* label, max length etc */
|
|
if (p15card && path != NULL && !(path->len & 1)) {
|
|
sc_path_t tmp_path = *path;
|
|
|
|
do {
|
|
r = sc_pkcs15_find_pin_by_reference(p15card,
|
|
&tmp_path, reference, &pin_obj);
|
|
tmp_path.len -= 2;
|
|
} while (r < 0 && tmp_path.len > 1);
|
|
if (pin_obj)
|
|
memcpy(&pin_info, pin_obj->data, sizeof(pin_info));
|
|
}
|
|
} else if (type == SC_AC_PRO) {
|
|
ident = "secure messaging key";
|
|
} else if (type == SC_AC_AUT) {
|
|
ident = "authentication key";
|
|
} else if (type == SC_AC_SYMBOLIC) {
|
|
/* This is a symbolic PIN name */
|
|
pin_id = reference;
|
|
switch (pin_id) {
|
|
case SC_PKCS15INIT_USER_PIN:
|
|
ident = "user PIN"; break;
|
|
case SC_PKCS15INIT_SO_PIN:
|
|
ident = "SO PIN"; break;
|
|
}
|
|
|
|
/* See if the card initializer set this PIN.
|
|
* If the reference is -1, he didn't, and any
|
|
* access conditions involving this pin should be
|
|
* ignored.
|
|
*/
|
|
reference = sc_keycache_find_named_pin(path, pin_id);
|
|
if (reference == -1) {
|
|
if (ctx->debug >= 2)
|
|
sc_debug(ctx, "no %s set for this card\n", ident);
|
|
SC_FUNC_RETURN(ctx, 3, SC_SUCCESS);
|
|
}
|
|
|
|
sc_profile_get_pin_info(pro, pin_id, &pin_info);
|
|
type = SC_AC_CHV;
|
|
}
|
|
|
|
#if 0
|
|
if (pinsize && pro->p15_data) {
|
|
sc_pkcs15_pincache_entry_t *entry = pro->p15_data->pin_cache[0];
|
|
if(entry) {
|
|
if (*pinsize >= entry->len) {
|
|
*pinsize = entry->len;
|
|
memcpy(pinbuf, entry->pin, entry->len);
|
|
goto found;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
/* Try to get the cached secret, e.g. CHV1 */
|
|
r = sc_keycache_get_key(path, type, reference, pinbuf, *pinsize);
|
|
if (r >= 0) {
|
|
*pinsize = r;
|
|
goto found;
|
|
}
|
|
|
|
if (type != SC_AC_CHV) {
|
|
/* Okay, nothing in our cache.
|
|
* Ask the card driver whether it knows a default key
|
|
* for this one.
|
|
*/
|
|
data.method = type;
|
|
data.key_ref = reference;
|
|
data.len = sizeof(defbuf);
|
|
data.key_data = defbuf;
|
|
if (sc_card_ctl(card, SC_CARDCTL_GET_DEFAULT_KEY, &data) >= 0)
|
|
defsize = data.len;
|
|
}
|
|
else 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(pro, pin_id, &pin_info, label,
|
|
pinbuf, pinsize);
|
|
}
|
|
break;
|
|
default:
|
|
if (callbacks.get_key) {
|
|
r = callbacks.get_key(pro, type, reference,
|
|
defbuf, defsize,
|
|
pinbuf, pinsize);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (r == SC_ERROR_OBJECT_NOT_FOUND) {
|
|
if (card->reader->capabilities & SC_READER_CAP_PIN_PAD) {
|
|
r = 0;
|
|
use_pinpad = 1;
|
|
}
|
|
else {
|
|
r = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;
|
|
}
|
|
}
|
|
|
|
SC_TEST_RET(ctx, r, "Failed to get secret");
|
|
|
|
/* We got something. Cache it */
|
|
sc_keycache_put_key(path, type, reference, pinbuf, *pinsize);
|
|
|
|
/* If it's a PIN, pad it out */
|
|
found: if (type == SC_AC_CHV && pin_info.flags & SC_PKCS15_PIN_FLAG_NEEDS_PADDING) {
|
|
int left = pro->pin_maxlen - *pinsize;
|
|
|
|
if (left > 0) {
|
|
memset(pinbuf + *pinsize, pro->pin_pad_char, left);
|
|
*pinsize = pro->pin_maxlen;
|
|
}
|
|
}
|
|
|
|
if (verify) {
|
|
/* We may have selected the AODF instead of the file
|
|
* itself: */
|
|
if (file) {
|
|
r = sc_select_file(card, &file->path, NULL);
|
|
SC_TEST_RET(ctx, r, "Failed to select PIN path");
|
|
}
|
|
|
|
if (use_pinpad)
|
|
r = sc_verify(card, type, reference, NULL, 0, NULL);
|
|
else
|
|
r = sc_verify(card, type, reference, pinbuf, *pinsize, NULL);
|
|
if (r < 0)
|
|
sc_debug(ctx, "Failed to verify %s (ref=0x%x)", ident, reference);
|
|
}
|
|
|
|
SC_FUNC_RETURN(ctx, 3, r);
|
|
}
|
|
|
|
static int
|
|
do_verify_pin(struct sc_profile *pro, sc_card_t *card, sc_file_t *file,
|
|
unsigned int type, unsigned int reference)
|
|
{
|
|
size_t pinsize;
|
|
u8 pinbuf[0x100];
|
|
|
|
pinsize = sizeof(pinbuf);
|
|
return do_get_and_verify_secret(pro, card, file, type, reference,
|
|
pinbuf, &pinsize, 1);
|
|
}
|
|
|
|
void
|
|
sc_pkcs15init_set_secret(struct sc_profile *pro,
|
|
int type, int reference,
|
|
u8 *key, size_t len)
|
|
{
|
|
sc_keycache_put_key(NULL, type, reference, key, len);
|
|
}
|
|
|
|
int
|
|
sc_pkcs15init_verify_key(struct sc_profile *pro, sc_card_t *card,
|
|
sc_file_t *file, unsigned int type, unsigned int reference)
|
|
{
|
|
size_t keysize;
|
|
u8 keybuf[64];
|
|
|
|
keysize = sizeof(keybuf);
|
|
return do_get_and_verify_secret(pro, card, file, type, reference,
|
|
keybuf, &keysize, 1);
|
|
}
|
|
|
|
/*
|
|
* Find out whether the card was initialized using an SO PIN,
|
|
* and if so, set the profile information
|
|
*/
|
|
static int set_so_pin_from_card(struct sc_pkcs15_card *p15card,
|
|
struct sc_profile *profile)
|
|
{
|
|
struct sc_pkcs15_pin_info *pin;
|
|
struct sc_pkcs15_object *obj;
|
|
int r;
|
|
|
|
r = sc_pkcs15_find_so_pin(p15card, &obj);
|
|
if (r == 0) {
|
|
pin = (struct sc_pkcs15_pin_info *) obj->data;
|
|
return sc_keycache_set_pin_name(&pin->path,
|
|
pin->reference,
|
|
SC_PKCS15INIT_SO_PIN);
|
|
}
|
|
|
|
/* If the card doesn't have an SO PIN, we simply zap the
|
|
* naming info from the cache */
|
|
if (r == SC_ERROR_OBJECT_NOT_FOUND)
|
|
return sc_keycache_set_pin_name(NULL, -1, SC_PKCS15INIT_SO_PIN);
|
|
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* If the user specified an auth_id, select the corresponding
|
|
* PIN entry and set the reference data.
|
|
* If auth_id is NULL, then get the first user PIN found, this
|
|
* is usefull for the 'onepin' profile option.
|
|
*/
|
|
static int
|
|
set_user_pin_from_authid(struct sc_pkcs15_card *p15card,
|
|
struct sc_profile *profile,
|
|
struct sc_pkcs15_id *auth_id)
|
|
{
|
|
struct sc_pkcs15_pin_info *pin;
|
|
struct sc_pkcs15_object *objp;
|
|
int r;
|
|
|
|
if (auth_id == NULL) {
|
|
int i;
|
|
struct sc_pkcs15_object *p15objects[5];
|
|
r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, p15objects, 5);
|
|
if (r < 0)
|
|
return r;
|
|
for (i = 0; i < r; i++) {
|
|
sc_pkcs15_pin_info_t *pininfo = (sc_pkcs15_pin_info_t *) p15objects[i]->data;
|
|
if (!(pininfo->flags & SC_PKCS15_PIN_FLAG_SO_PIN)) {
|
|
auth_id = &pininfo->auth_id;
|
|
break;
|
|
}
|
|
}
|
|
if (i >= r)
|
|
return SC_ERROR_OBJECT_NOT_FOUND;
|
|
}
|
|
|
|
if (auth_id->len == 0)
|
|
return 0;
|
|
|
|
r = sc_pkcs15_find_pin_by_auth_id(p15card, auth_id, &objp);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
pin = (struct sc_pkcs15_pin_info *) objp->data;
|
|
|
|
/* If the PIN resides in a separate directory, make sure the
|
|
* profile defines the DF. Otherwise, generate a file object
|
|
* on the fly (XXX hack attack)
|
|
*
|
|
* Possible fix: store all file info from the profile on the card
|
|
*/
|
|
if (pin->path.len != 0) {
|
|
sc_file_t *df = NULL;
|
|
|
|
r = sc_profile_get_file_by_path(profile, &pin->path, &df);
|
|
if (r == SC_ERROR_FILE_NOT_FOUND
|
|
&& (r = sc_select_file(p15card->card, &pin->path, &df)) == 0) {
|
|
sc_profile_add_file(profile, "pin-dir (auto)", df);
|
|
}
|
|
|
|
if (df)
|
|
sc_file_free(df);
|
|
}
|
|
|
|
return sc_keycache_set_pin_name(&pin->path,
|
|
pin->reference, SC_PKCS15INIT_USER_PIN);
|
|
}
|
|
|
|
/*
|
|
* 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 *pro, sc_card_t *card,
|
|
sc_file_t *file, int op)
|
|
{
|
|
const sc_acl_entry_t *acl;
|
|
sc_file_t *file_tmp = NULL;
|
|
int r = 0;
|
|
char pbuf[SC_MAX_PATH_STRING_SIZE];
|
|
|
|
r = sc_path_print(pbuf, sizeof(pbuf), &file->path);
|
|
if (r != SC_SUCCESS)
|
|
pbuf[0] = '\0';
|
|
|
|
sc_debug(card->ctx, "path=%s, op=%u\n", pbuf, op);
|
|
|
|
if (card->caps & SC_CARD_CAP_USE_FCI_AC) {
|
|
if ((r = sc_select_file(card, &file->path, &file_tmp)) < 0)
|
|
return r;
|
|
acl = sc_file_get_acl_entry(file_tmp, op);
|
|
}
|
|
else
|
|
acl = sc_file_get_acl_entry(file, op);
|
|
|
|
sc_debug(card->ctx, "r:[0x%08x]\n",r);
|
|
sc_debug(card->ctx, "acl:[0x%08x]\n",acl);
|
|
|
|
for (; r == 0 && acl; acl = acl->next) {
|
|
if (acl->method == SC_AC_NEVER)
|
|
{
|
|
sc_debug(card->ctx, "never\n");
|
|
return SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;
|
|
}
|
|
if (acl->method == SC_AC_NONE)
|
|
{
|
|
sc_debug(card->ctx, "none\n");
|
|
break;
|
|
}
|
|
if (acl->method == SC_AC_UNKNOWN) {
|
|
sc_debug(card->ctx, "unknown acl method\n");
|
|
break;
|
|
}
|
|
sc_debug(card->ctx, "verify\n");
|
|
r = do_verify_pin(pro, card, file_tmp ? file_tmp : file,
|
|
acl->method, acl->key_ref);
|
|
}
|
|
|
|
if (file_tmp)
|
|
sc_file_free(file_tmp);
|
|
|
|
return r;
|
|
}
|
|
|
|
static int do_select_parent(struct sc_profile *pro, sc_card_t *card,
|
|
sc_file_t *file, sc_file_t **parent)
|
|
{
|
|
struct sc_path path;
|
|
int r;
|
|
|
|
/* Get the parent's path */
|
|
path = file->path;
|
|
if (path.len >= 2)
|
|
path.len -= 2;
|
|
if (path.len == 0)
|
|
sc_format_path("3F00", &path);
|
|
|
|
/* Select the parent DF. */
|
|
*parent = NULL;
|
|
r = sc_select_file(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(pro, &path, parent);
|
|
if (r < 0) {
|
|
char pbuf[SC_MAX_PATH_STRING_SIZE];
|
|
|
|
r = sc_path_print(pbuf, sizeof(pbuf), &path);
|
|
if (r != SC_SUCCESS)
|
|
pbuf[0] = '\0';
|
|
|
|
sc_debug(card->ctx,
|
|
"profile doesn't define a DF %s", pbuf);
|
|
return r;
|
|
}
|
|
if (!(r = sc_pkcs15init_create_file(pro, card, *parent)))
|
|
r = sc_select_file(card, &path, NULL);
|
|
} else if (r == SC_SUCCESS && !strcmp(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(pro, &path, parent);
|
|
if (r < 0) {
|
|
char pbuf[SC_MAX_PATH_STRING_SIZE];
|
|
|
|
r = sc_path_print(pbuf, sizeof(pbuf), &path);
|
|
if (r != SC_SUCCESS)
|
|
pbuf[0] = '\0';
|
|
|
|
sc_debug(card->ctx,
|
|
"profile doesn't define a DF %s", pbuf);
|
|
return r;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
int
|
|
sc_pkcs15init_create_file(struct sc_profile *pro, sc_card_t *card,
|
|
sc_file_t *file)
|
|
{
|
|
struct sc_file *parent = NULL;
|
|
int r;
|
|
|
|
/* Select parent DF and verify PINs/key as necessary */
|
|
if ((r = do_select_parent(pro, card, file, &parent)) < 0
|
|
|| (r = sc_pkcs15init_authenticate(pro, card,
|
|
parent, SC_AC_OP_CREATE)) < 0)
|
|
goto out;
|
|
|
|
/* Fix up the file's ACLs */
|
|
if ((r = sc_pkcs15init_fixup_file(pro, file)) < 0)
|
|
return r;
|
|
|
|
/* ensure we are in the correct lifecycle */
|
|
r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN);
|
|
if (r < 0 && r != SC_ERROR_NOT_SUPPORTED)
|
|
return r;
|
|
|
|
r = sc_create_file(card, file);
|
|
|
|
out: if (parent)
|
|
sc_file_free(parent);
|
|
return r;
|
|
}
|
|
|
|
int
|
|
sc_pkcs15init_update_file(struct sc_profile *profile, sc_card_t *card,
|
|
sc_file_t *file, void *data, unsigned int datalen)
|
|
{
|
|
struct sc_file *info = NULL;
|
|
void *copy = NULL;
|
|
int r, need_to_zap = 0;
|
|
char pbuf[SC_MAX_PATH_STRING_SIZE];
|
|
|
|
r = sc_path_print(pbuf, sizeof(pbuf), &file->path);
|
|
if (r != SC_SUCCESS)
|
|
pbuf[0] = '\0';
|
|
sc_debug(card->ctx, "called, path=%s, %u bytes\n", pbuf, datalen);
|
|
|
|
if ((r = sc_select_file(card, &file->path, &info)) < 0) {
|
|
/* Create file if it doesn't exist */
|
|
if (file->size < datalen)
|
|
file->size = datalen;
|
|
if (r != SC_ERROR_FILE_NOT_FOUND
|
|
|| (r = sc_pkcs15init_create_file(profile, card, file)) < 0
|
|
|| (r = sc_select_file(card, &file->path, &info)) < 0)
|
|
return r;
|
|
} else {
|
|
need_to_zap = 1;
|
|
}
|
|
|
|
if (info->size < datalen) {
|
|
r = sc_path_print(pbuf, sizeof(pbuf), &file->path);
|
|
if (r != SC_SUCCESS)
|
|
pbuf[0] = '\0';
|
|
|
|
sc_debug(card->ctx,
|
|
"File %s too small (require %u, have %u) - "
|
|
"please increase size in profile", pbuf,
|
|
datalen, info->size);
|
|
sc_file_free(info);
|
|
return SC_ERROR_TOO_MANY_OBJECTS;
|
|
} else if (info->size > datalen && need_to_zap) {
|
|
/* zero out the rest of the file - we may have shrunk
|
|
* the file contents */
|
|
copy = calloc(1, info->size);
|
|
if (copy == NULL) {
|
|
sc_file_free(info);
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
memcpy(copy, data, datalen);
|
|
datalen = info->size;
|
|
data = copy;
|
|
}
|
|
|
|
/* Present authentication info needed */
|
|
r = sc_pkcs15init_authenticate(profile, card, file, SC_AC_OP_UPDATE);
|
|
|
|
if (r >= 0 && datalen)
|
|
r = sc_update_binary(card, 0, (const u8 *) data, datalen, 0);
|
|
|
|
if (copy)
|
|
free(copy);
|
|
sc_file_free(info);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Fix up all file ACLs
|
|
*/
|
|
int
|
|
sc_pkcs15init_fixup_file(struct sc_profile *profile, sc_file_t *file)
|
|
{
|
|
sc_context_t *ctx = profile->card->ctx;
|
|
sc_acl_entry_t so_acl, user_acl;
|
|
unsigned int op, needfix = 0;
|
|
int ref;
|
|
|
|
/* 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 sc_acl_entry_t *acl;
|
|
|
|
acl = sc_file_get_acl_entry(file, op);
|
|
for (; acl; acl = acl->next) {
|
|
if (acl->method == SC_AC_SYMBOLIC)
|
|
needfix++;
|
|
}
|
|
}
|
|
|
|
if (!needfix)
|
|
return 0;
|
|
|
|
/* If the profile doesn't specify a SO pin, change all
|
|
* ACLs that reference $sopin to NONE */
|
|
ref = sc_keycache_find_named_pin(&file->path, SC_PKCS15INIT_SO_PIN);
|
|
if (ref < 0) {
|
|
so_acl.method = SC_AC_NONE;
|
|
so_acl.key_ref = 0;
|
|
} else {
|
|
if (ctx->debug >= 2) {
|
|
sc_debug(ctx,
|
|
"sc_pkcs15init_fixup_file: SO pin is CVH%d\n",
|
|
ref);
|
|
}
|
|
so_acl.method = SC_AC_CHV;
|
|
so_acl.key_ref = ref;
|
|
}
|
|
|
|
ref = sc_keycache_find_named_pin(&file->path, SC_PKCS15INIT_USER_PIN);
|
|
if (ref < 0) {
|
|
user_acl.method = SC_AC_NONE;
|
|
user_acl.key_ref = 0;
|
|
} else {
|
|
if (ctx->debug >= 2) {
|
|
sc_debug(ctx,
|
|
"sc_pkcs15init_fixup_file: user pin is CVH%d\n",
|
|
ref);
|
|
}
|
|
user_acl.method = SC_AC_CHV;
|
|
user_acl.key_ref = ref;
|
|
}
|
|
|
|
return sc_pkcs15init_fixup_acls(profile, file, &so_acl, &user_acl);
|
|
}
|
|
|
|
/*
|
|
* Fix up a file's ACLs by replacing all occurrences of a symbolic
|
|
* PIN name with the real reference.
|
|
*/
|
|
int
|
|
sc_pkcs15init_fixup_acls(struct sc_profile *profile, sc_file_t *file,
|
|
sc_acl_entry_t *so_acl,
|
|
sc_acl_entry_t *user_acl)
|
|
{
|
|
sc_card_t *card = profile->card;
|
|
sc_acl_entry_t acls[16];
|
|
unsigned int op, num;
|
|
int r = 0;
|
|
|
|
for (op = 0; r == 0 && op < SC_MAX_AC_OPS; op++) {
|
|
const sc_acl_entry_t *acl;
|
|
const char *what;
|
|
int added = 0;
|
|
|
|
/* First, get original ACLs */
|
|
acl = sc_file_get_acl_entry(file, op);
|
|
for (num = 0; num < 16 && acl; num++, acl = acl->next)
|
|
acls[num] = *acl;
|
|
|
|
sc_file_clear_acl_entries(file, op);
|
|
for (acl = acls; acl < acls + num; acl++) {
|
|
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_debug(card->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 == NULL || acl->key_ref == (unsigned int)-1) {
|
|
sc_debug(card->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);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static int
|
|
sc_pkcs15init_get_pin_path(sc_pkcs15_card_t *p15card,
|
|
sc_pkcs15_id_t *auth_id, sc_path_t *path)
|
|
{
|
|
sc_pkcs15_object_t *obj;
|
|
int r;
|
|
|
|
r = sc_pkcs15_find_pin_by_auth_id(p15card, auth_id, &obj);
|
|
if (r < 0)
|
|
return r;
|
|
*path = ((sc_pkcs15_pin_info_t *) obj->data)->path;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sc_pkcs15init_get_pin_info(struct sc_profile *profile,
|
|
unsigned int id, struct sc_pkcs15_pin_info *pin)
|
|
{
|
|
sc_profile_get_pin_info(profile, id, pin);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sc_pkcs15init_get_manufacturer(struct sc_profile *profile, const char **res)
|
|
{
|
|
*res = profile->p15_spec->manufacturer_id;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sc_pkcs15init_get_serial(struct sc_profile *profile, const char **res)
|
|
{
|
|
*res = profile->p15_spec->serial_number;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sc_pkcs15init_set_pin_data(sc_profile_t *profile, int id,
|
|
const u8 *key, size_t len)
|
|
{
|
|
return sc_keycache_put_key(NULL, SC_AC_SYMBOLIC, id, key, len);
|
|
}
|
|
|
|
int
|
|
sc_pkcs15init_set_serial(struct sc_profile *profile, const char *serial)
|
|
{
|
|
if (profile->p15_spec->serial_number)
|
|
free(profile->p15_spec->serial_number);
|
|
profile->p15_spec->serial_number = strdup(serial);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
sc_pkcs15init_get_label(struct sc_profile *profile, const char **res)
|
|
{
|
|
*res = profile->p15_spec->label;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sc_pkcs15init_qualify_pin(sc_card_t *card, const char *pin_name,
|
|
unsigned int pin_len, sc_pkcs15_pin_info_t *pin_info)
|
|
{
|
|
if (pin_len == 0)
|
|
return 0;
|
|
if (pin_len < pin_info->min_length) {
|
|
sc_debug(card->ctx, "%s too short (min length %u)",
|
|
pin_name, pin_info->min_length);
|
|
return SC_ERROR_WRONG_LENGTH;
|
|
}
|
|
if (pin_len > pin_info->max_length) {
|
|
sc_debug(card->ctx, "%s too long (max length %u)",
|
|
pin_name, pin_info->max_length);
|
|
return SC_ERROR_WRONG_LENGTH;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get the list of options from the card, if it specifies them
|
|
*/
|
|
static int
|
|
sc_pkcs15init_read_info(sc_card_t *card, sc_profile_t *profile)
|
|
{
|
|
sc_path_t path;
|
|
sc_file_t *file = NULL;
|
|
u8 *mem = NULL;
|
|
size_t len = 0;
|
|
int r;
|
|
|
|
sc_format_path(OPENSC_INFO_FILEPATH, &path);
|
|
if ((r = sc_select_file(card, &path, &file)) >= 0) {
|
|
len = file->size;
|
|
sc_file_free(file);
|
|
r = SC_ERROR_OUT_OF_MEMORY;
|
|
if ((mem = (u8 *) malloc(len)) != NULL) {
|
|
r = sc_read_binary(card, 0, mem, len, 0);
|
|
}
|
|
} 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 = (char *) malloc(len+1)))
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
memcpy(s, p, len);
|
|
s[len] = '\0';
|
|
if (*strp)
|
|
free(*strp);
|
|
*strp = s;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 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(sc_card_t *card,
|
|
const u8 *p, size_t len,
|
|
sc_profile_t *profile)
|
|
{
|
|
u8 tag;
|
|
const u8 *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_debug(card->ctx,
|
|
"Too many options in OpenSC Info file\n");
|
|
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_debug(card->ctx, "OpenSC info file corrupted\n");
|
|
return SC_ERROR_PKCS15INIT;
|
|
}
|
|
|
|
static int
|
|
do_encode_string(u8 **memp, u8 *end, u8 tag, const char *s)
|
|
{
|
|
u8 *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(sc_card_t *card, sc_profile_t *profile,
|
|
sc_pkcs15_object_t *pin_obj)
|
|
{
|
|
sc_file_t *file = NULL;
|
|
sc_file_t *df = profile->df_info->file;
|
|
u8 buffer[512], *p, *end;
|
|
unsigned int method;
|
|
unsigned long key_ref;
|
|
int n, r;
|
|
|
|
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;
|
|
|
|
if (pin_obj != NULL) {
|
|
method = SC_AC_CHV;
|
|
key_ref = ((sc_pkcs15_pin_info_t *) pin_obj->data)->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) {
|
|
file->size = p - buffer;
|
|
if (file->size < 128)
|
|
file->size = 128;
|
|
r = sc_pkcs15init_update_file(profile, card, file, buffer, p - buffer);
|
|
}
|
|
|
|
sc_file_free(file);
|
|
return r;
|
|
}
|