opensc/src/pkcs15init/pkcs15-lib.c

3877 lines
100 KiB
C
Raw Normal View History

/*
* 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>
Complete rewrite of OpenSC build system. 1. Build system now supports MinGW (Windows) compilation using msys and cross compilation. 2. Ability to explicitly disable and enable dependencies of the package. 3. openct, pcsc and nsplugins features are disabled by default. 4. Modified pcsc driver to use pcsc dynamically, no compile time dependency is required. 5. --enable-pcsc-lite configuration option renamed to --enable-pcsc. 6. Install opensc.conf file (as opensc.conf.new if opensc.conf exists). 7. Add--enable-doc configuration option, allow installing documentation into target. 8. Add --disable-man configuration option, allow msys mingw32 users to build from svn without extra dependencies. 9. Add export files to each library in order to export only required symbols. Windows native build may use these files instead of scanning objects' symbols. 10. Add opensc-tool --info to display some general information about the build. 11. Create compatibility library to be linked against library instread of recompiling the same source files in different places. 12. Add different win32 version resource to each class of outputs. 13. Make xsl-stylesheets location selectable. 14. Some win32 fixups. 15. Some warning fixups. 16. Many other autoconf/automake cleanups. Alon Bar-Lev svn diff -r 3315:3399 https://www.opensc-project.org/svn/opensc/branches/alonbl/mingw _M . D configure.in _M src _M src/openssh M src/openssh/Makefile.am _M src/tools M src/tools/rutoken-tool.c M src/tools/opensc-tool.c M src/tools/cardos-info.c M src/tools/pkcs15-crypt.c M src/tools/pkcs15-init.c M src/tools/piv-tool.c M src/tools/netkey-tool.c M src/tools/eidenv.c M src/tools/cryptoflex-tool.c M src/tools/util.c M src/tools/pkcs11-tool.c M src/tools/pkcs15-tool.c M src/tools/util.h M src/tools/opensc-explorer.c M src/tools/Makefile.am _M src/pkcs11 M src/pkcs11/pkcs11-global.c M src/pkcs11/framework-pkcs15.c M src/pkcs11/mechanism.c M src/pkcs11/pkcs11-display.c M src/pkcs11/pkcs11-object.c A src/pkcs11/opensc-pkcs11.exports M src/pkcs11/sc-pkcs11.h M src/pkcs11/pkcs11-spy.c M src/pkcs11/openssl.c M src/pkcs11/Makefile.am A src/pkcs11/pkcs11-spy.exports _M src/tests _M src/tests/regression M src/tests/regression/Makefile.am M src/tests/sc-test.c M src/tests/pintest.c M src/tests/Makefile.am _M src/include _M src/include/opensc M src/include/opensc/Makefile.am A src/include/opensc/svnignore M src/include/Makefile.am _M src/signer _M src/signer/npinclude M src/signer/npinclude/Makefile.am M src/signer/Makefile.am A src/signer/signer.exports _M src/common A src/common/compat_dummy.c D src/common/getopt.txt D src/common/strlcpy.c D src/common/LICENSE A src/common/compat_getopt.txt A src/common/compat_strlcpy.c A src/common/LICENSE.compat_getopt A src/common/compat_getopt.c D src/common/strlcpy.h D src/common/ChangeLog D src/common/getpass.c D src/common/my_getopt.c A src/common/compat_strlcpy.h A src/common/compat_getpass.c A src/common/compat_getopt.h A src/common/ChangeLog.compat_getopt D src/common/README.strlcpy D src/common/my_getopt.h A src/common/compat_getpass.h A src/common/README.compat_strlcpy D src/common/strlcpy.3 A src/common/README.compat_getopt D src/common/getopt.3 D src/common/README.my_getopt A src/common/compat_strlcpy.3 A src/common/compat_getopt.3 M src/common/Makefile.am M src/Makefile.am _M src/pkcs15init M src/pkcs15init/pkcs15-oberthur.c M src/pkcs15init/profile.c M src/pkcs15init/pkcs15-lib.c M src/pkcs15init/pkcs15-rutoken.c A src/pkcs15init/pkcs15init.exports M src/pkcs15init/pkcs15-gpk.c M src/pkcs15init/Makefile.am _M src/scconf M src/scconf/Makefile.am M src/scconf/parse.c A src/scconf/scconf.exports _M src/libopensc M src/libopensc/card-rutoken.c M src/libopensc/compression.c M src/libopensc/sc.c M src/libopensc/card-piv.c M src/libopensc/pkcs15-openpgp.c M src/libopensc/pkcs15-postecert.c M src/libopensc/pkcs15-tcos.c M src/libopensc/opensc-config.in M src/libopensc/reader-pcsc.c A src/libopensc/internal-winscard.h M src/libopensc/ctx.c A src/libopensc/libopensc.exports M src/libopensc/pkcs15-piv.c M src/libopensc/pkcs15-infocamere.c M src/libopensc/internal.h M src/libopensc/pkcs15-actalis.c M src/libopensc/pkcs15-starcert.c M src/libopensc/card-oberthur.c M src/libopensc/pkcs15-atrust-acos.c M src/libopensc/p15card-helper.c D src/libopensc/part10.h M src/libopensc/ui.c M src/libopensc/card-gpk.c M src/libopensc/pkcs15-wrap.c M src/libopensc/pkcs15-gemsafeGPK.c M src/libopensc/log.c M src/libopensc/pkcs15-esteid.c M src/libopensc/pkcs15-prkey-rutoken.c M src/libopensc/log.h M src/libopensc/Makefile.am M src/libopensc/reader-openct.c _M aclocal M aclocal/Makefile.am _M win32 M win32/Makefile.am A win32/versioninfo.rc.in A win32/ltrc.inc A configure.ac _M doc _M doc/tools M doc/tools/pkcs15-profile.xml D doc/changelog.sh D doc/export-wiki.xsl _M doc/api _M doc/api/file M doc/api/man.xsl _M doc/api/asn1 _M doc/api/apps _M doc/api/init _M doc/api/types _M doc/api/card M doc/api/html.xsl _M doc/api/misc _M doc/api/util M doc/Makefile.am D doc/export-wiki.sh AM doc/nonpersistent A doc/nonpersistent/export-wiki.xsl A doc/nonpersistent/Makefile.am A doc/nonpersistent/export-wiki.sh A doc/nonpersistent/svn2cl.xsl D doc/generate-man.sh D doc/svn2cl.xsl M Makefile.am A svnignore _M etc M etc/opensc.conf.in M etc/Makefile.am D man _M solaris M solaris/Makefile git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3405 c6295689-39f2-0310-b995-f0e70906c6a9
2008-03-06 16:06:59 +00:00
#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>
Complete rewrite of OpenSC build system. 1. Build system now supports MinGW (Windows) compilation using msys and cross compilation. 2. Ability to explicitly disable and enable dependencies of the package. 3. openct, pcsc and nsplugins features are disabled by default. 4. Modified pcsc driver to use pcsc dynamically, no compile time dependency is required. 5. --enable-pcsc-lite configuration option renamed to --enable-pcsc. 6. Install opensc.conf file (as opensc.conf.new if opensc.conf exists). 7. Add--enable-doc configuration option, allow installing documentation into target. 8. Add --disable-man configuration option, allow msys mingw32 users to build from svn without extra dependencies. 9. Add export files to each library in order to export only required symbols. Windows native build may use these files instead of scanning objects' symbols. 10. Add opensc-tool --info to display some general information about the build. 11. Create compatibility library to be linked against library instread of recompiling the same source files in different places. 12. Add different win32 version resource to each class of outputs. 13. Make xsl-stylesheets location selectable. 14. Some win32 fixups. 15. Some warning fixups. 16. Many other autoconf/automake cleanups. Alon Bar-Lev svn diff -r 3315:3399 https://www.opensc-project.org/svn/opensc/branches/alonbl/mingw _M . D configure.in _M src _M src/openssh M src/openssh/Makefile.am _M src/tools M src/tools/rutoken-tool.c M src/tools/opensc-tool.c M src/tools/cardos-info.c M src/tools/pkcs15-crypt.c M src/tools/pkcs15-init.c M src/tools/piv-tool.c M src/tools/netkey-tool.c M src/tools/eidenv.c M src/tools/cryptoflex-tool.c M src/tools/util.c M src/tools/pkcs11-tool.c M src/tools/pkcs15-tool.c M src/tools/util.h M src/tools/opensc-explorer.c M src/tools/Makefile.am _M src/pkcs11 M src/pkcs11/pkcs11-global.c M src/pkcs11/framework-pkcs15.c M src/pkcs11/mechanism.c M src/pkcs11/pkcs11-display.c M src/pkcs11/pkcs11-object.c A src/pkcs11/opensc-pkcs11.exports M src/pkcs11/sc-pkcs11.h M src/pkcs11/pkcs11-spy.c M src/pkcs11/openssl.c M src/pkcs11/Makefile.am A src/pkcs11/pkcs11-spy.exports _M src/tests _M src/tests/regression M src/tests/regression/Makefile.am M src/tests/sc-test.c M src/tests/pintest.c M src/tests/Makefile.am _M src/include _M src/include/opensc M src/include/opensc/Makefile.am A src/include/opensc/svnignore M src/include/Makefile.am _M src/signer _M src/signer/npinclude M src/signer/npinclude/Makefile.am M src/signer/Makefile.am A src/signer/signer.exports _M src/common A src/common/compat_dummy.c D src/common/getopt.txt D src/common/strlcpy.c D src/common/LICENSE A src/common/compat_getopt.txt A src/common/compat_strlcpy.c A src/common/LICENSE.compat_getopt A src/common/compat_getopt.c D src/common/strlcpy.h D src/common/ChangeLog D src/common/getpass.c D src/common/my_getopt.c A src/common/compat_strlcpy.h A src/common/compat_getpass.c A src/common/compat_getopt.h A src/common/ChangeLog.compat_getopt D src/common/README.strlcpy D src/common/my_getopt.h A src/common/compat_getpass.h A src/common/README.compat_strlcpy D src/common/strlcpy.3 A src/common/README.compat_getopt D src/common/getopt.3 D src/common/README.my_getopt A src/common/compat_strlcpy.3 A src/common/compat_getopt.3 M src/common/Makefile.am M src/Makefile.am _M src/pkcs15init M src/pkcs15init/pkcs15-oberthur.c M src/pkcs15init/profile.c M src/pkcs15init/pkcs15-lib.c M src/pkcs15init/pkcs15-rutoken.c A src/pkcs15init/pkcs15init.exports M src/pkcs15init/pkcs15-gpk.c M src/pkcs15init/Makefile.am _M src/scconf M src/scconf/Makefile.am M src/scconf/parse.c A src/scconf/scconf.exports _M src/libopensc M src/libopensc/card-rutoken.c M src/libopensc/compression.c M src/libopensc/sc.c M src/libopensc/card-piv.c M src/libopensc/pkcs15-openpgp.c M src/libopensc/pkcs15-postecert.c M src/libopensc/pkcs15-tcos.c M src/libopensc/opensc-config.in M src/libopensc/reader-pcsc.c A src/libopensc/internal-winscard.h M src/libopensc/ctx.c A src/libopensc/libopensc.exports M src/libopensc/pkcs15-piv.c M src/libopensc/pkcs15-infocamere.c M src/libopensc/internal.h M src/libopensc/pkcs15-actalis.c M src/libopensc/pkcs15-starcert.c M src/libopensc/card-oberthur.c M src/libopensc/pkcs15-atrust-acos.c M src/libopensc/p15card-helper.c D src/libopensc/part10.h M src/libopensc/ui.c M src/libopensc/card-gpk.c M src/libopensc/pkcs15-wrap.c M src/libopensc/pkcs15-gemsafeGPK.c M src/libopensc/log.c M src/libopensc/pkcs15-esteid.c M src/libopensc/pkcs15-prkey-rutoken.c M src/libopensc/log.h M src/libopensc/Makefile.am M src/libopensc/reader-openct.c _M aclocal M aclocal/Makefile.am _M win32 M win32/Makefile.am A win32/versioninfo.rc.in A win32/ltrc.inc A configure.ac _M doc _M doc/tools M doc/tools/pkcs15-profile.xml D doc/changelog.sh D doc/export-wiki.xsl _M doc/api _M doc/api/file M doc/api/man.xsl _M doc/api/asn1 _M doc/api/apps _M doc/api/init _M doc/api/types _M doc/api/card M doc/api/html.xsl _M doc/api/misc _M doc/api/util M doc/Makefile.am D doc/export-wiki.sh AM doc/nonpersistent A doc/nonpersistent/export-wiki.xsl A doc/nonpersistent/Makefile.am A doc/nonpersistent/export-wiki.sh A doc/nonpersistent/svn2cl.xsl D doc/generate-man.sh D doc/svn2cl.xsl M Makefile.am A svnignore _M etc M etc/opensc.conf.in M etc/Makefile.am D man _M solaris M solaris/Makefile git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3405 c6295689-39f2-0310-b995-f0e70906c6a9
2008-03-06 16:06:59 +00:00
#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
/* 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_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;
p15spec->card = card;
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_debug(card->ctx, "Too many applications on this card.");
return SC_ERROR_TOO_MANY_OBJECTS;
}
/* 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);
if (r < 0)
return r;
/* Select the PIN reference */
pin_info.path = df->path;
if (profile->ops->select_pin_reference) {
r = profile->ops->select_pin_reference(profile,
card, &pin_info);
if (r < 0)
return r;
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);
if (r < 0)
return r;
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)) < 0) {
if (pin_obj)
sc_pkcs15_free_object(pin_obj);
return r;
}
/* Create the application DF and store the PINs */
if (profile->ops->create_dir) {
/* Create the 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) {
if (pin_obj)
sc_pkcs15_free_object(pin_obj);
return r;
}
/* 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)
return SC_ERROR_OUT_OF_MEMORY;
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);
return 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_pin(struct sc_pkcs15_card *p15card,
struct sc_profile *profile,
struct sc_pkcs15init_pinargs *args)
{
sc_card_t *card = p15card->card;
sc_pkcs15_object_t *pin_obj;
sc_pkcs15_pin_info_t *pin_info;
int r, idx;
/* No auth_id given: select one */
if (args->auth_id.len == 0) {
struct sc_pkcs15_object *dummy;
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, &dummy);
if (r == SC_ERROR_OBJECT_NOT_FOUND)
break;
}
if (r != SC_ERROR_OBJECT_NOT_FOUND) {
sc_debug(card->ctx, "No auth_id specified for new PIN");
return SC_ERROR_INVALID_ARGUMENTS;
}
} else {
struct sc_pkcs15_object *dummy;
/* Make sure we don't get duplicate PIN IDs */
r = sc_pkcs15_find_pin_by_auth_id(p15card, &args->auth_id, &dummy);
if (r != SC_ERROR_OBJECT_NOT_FOUND) {
sc_debug(card->ctx, "There already is a PIN with this ID.");
return SC_ERROR_INVALID_ARGUMENTS;
}
}
pin_obj = sc_pkcs15init_new_object(SC_PKCS15_TYPE_AUTH_PIN,
args->label, NULL, NULL);
if (pin_obj == NULL)
return SC_ERROR_OUT_OF_MEMORY;
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 */
if ((r = set_so_pin_from_card(p15card, profile)) < 0) {
sc_pkcs15_free_object(pin_obj);
return r;
}
/* 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, card, pin_info, idx,
args->pin, args->pin_len,
args->puk, args->puk_len);
}
/* 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_PIN);
}
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;
return 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)
{
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;
/* 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_debug(card->ctx, "PIN domains not supported.");
return SC_ERROR_NOT_SUPPORTED;
}
r = profile->ops->create_domain(profile, card,
&pin_info->auth_id, &df);
if (r < 0)
return r;
}
pin_info->path = df->path;
pin_info->reference = 0;
/* Loop until we come up with an acceptable pin reference */
while (1) {
sc_pkcs15_object_t *dummy;
if (profile->ops->select_pin_reference) {
r = profile->ops->select_pin_reference(profile, card, pin_info);
if (r < 0)
return r;
retry = 1;
}
r = sc_pkcs15_find_pin_by_reference(p15card,
&pin_info->path,
pin_info->reference, &dummy);
if (r == SC_ERROR_OBJECT_NOT_FOUND)
break;
if (r != 0 || !retry) {
/* Other error trying to retrieve pin obj */
sc_debug(card->ctx, "Failed to allocate PIN reference.");
return SC_ERROR_TOO_MANY_OBJECTS;
}
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;
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);
return 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_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;
if (!res_obj || !keybits)
return SC_ERROR_INVALID_ARGUMENTS;
*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)
return SC_ERROR_OUT_OF_MEMORY;
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);
if (r < 0)
return r;
}
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)
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;
}
r = select_object_path(p15card, profile, object,
&key_info->id, &key_info->path);
if (r < 0)
return r;
/* 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);
if (r < 0)
return r;
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_debug(card->ctx,
"Failed to select key reference.");
return SC_ERROR_TOO_MANY_OBJECTS;
}
key_info->key_reference++;
}
}
*res_obj = object;
return 0;
}
/*
* 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_pkcs15init_pubkeyargs pubkey_args;
struct sc_pkcs15_object *object;
struct sc_pkcs15_prkey_info *key_info;
int r, caller_supplied_id = 0;
/* check supported key size */
r = check_key_size(p15card->card, keygen_args->prkey_args.key.algorithm, keybits);
if (r != SC_SUCCESS)
return r;
/* 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))
return SC_ERROR_NOT_SUPPORTED;
if (profile->ops->generate_key == NULL && profile->ops->old_generate_key == NULL)
return SC_ERROR_NOT_SUPPORTED;
/* Set the USER PIN reference from args */
r = set_user_pin_from_authid(p15card, profile,
&keygen_args->prkey_args.auth_id);
if (r < 0)
return r;
/* Set the SO PIN reference from card */
if ((r = set_so_pin_from_card(p15card, profile)) < 0)
return r;
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);
if (r < 0)
return r;
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);
if (r < 0)
return r;
r = profile->ops->generate_key(profile, p15card->card,
object, &pubkey_args.key);
if (r < 0)
return r;
} 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);
}
/* update PrKDF entry */
if (r >= 0) {
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);
if (r < 0)
return r;
if (iid.len) {
key_info->id = iid;
pubkey_args.id = iid;
}
}
r = sc_pkcs15init_add_object(p15card, profile, SC_PKCS15_PRKDF, object);
}
if (r >= 0) {
sc_pkcs15_object_t *pub_object;
r = sc_pkcs15init_store_public_key(p15card, profile,
&pubkey_args, &pub_object);
}
if (r >= 0 && res_obj)
*res_obj = object;
sc_pkcs15_erase_pubkey(&pubkey_args.key);
profile->dirty = 1;
return 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_pkcs15_object *object;
struct sc_pkcs15_prkey_info *key_info;
struct sc_card *card = p15card->card;
struct sc_pkcs15_prkey key;
int keybits, idx, r = 0;
/* Create a copy of the key first */
key = keyargs->key;
if ((r = prkey_fixup(p15card, &key)) < 0)
return r;
if ((keybits = prkey_bits(p15card, &key)) < 0)
return keybits;
/* 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_debug(card->ctx, "Card does not support this key.");
return SC_ERROR_INCOMPATIBLE_KEY;
}
if (!keyargs->passphrase
&& !(keyargs->flags & SC_PKCS15INIT_NO_PASSPHRASE)) {
sc_debug(card->ctx,
"No key encryption passphrase given.");
return SC_ERROR_PASSPHRASE_REQUIRED;
}
}
/* Set the USER PIN reference from args */
r = set_user_pin_from_authid(p15card, profile, &keyargs->auth_id);
if (r < 0)
return r;
/* Set the SO PIN reference from card */
if ((r = set_so_pin_from_card(p15card, profile)) < 0)
return r;
/* 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);
if (r < 0)
return r;
/* Set up the PrKDF object */
r = sc_pkcs15init_init_prkdf(p15card, profile, keyargs, &key, keybits, &object);
if (r < 0)
return r;
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);
if (r < 0)
return r;
r = profile->ops->store_key(profile, p15card->card,
object, &key);
if (r < 0)
return r;
} else {
r = profile->ops->new_key(profile, p15card->card,
&key, idx, key_info);
if (r < 0)
return r;
}
} 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);
if (r < 0)
return r;
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);
return r;
}
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);
if (r < 0)
return r;
}
/* 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;
return 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)
{
unsigned int usage = keyargs->x509_usage;
int r;
/* 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;
return r;
}
/*
* Store a public key
*/
int
sc_pkcs15init_store_public_key(struct sc_pkcs15_card *p15card,
struct sc_profile *profile,
struct sc_pkcs15init_pubkeyargs *keyargs,
struct sc_pkcs15_object **res_obj)
{
struct sc_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;
if (!res_obj || !keyargs)
return SC_ERROR_NOT_SUPPORTED;
/* 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_debug(p15card->card->ctx, "Unsupported key algorithm.\n");
return SC_ERROR_NOT_SUPPORTED;
}
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)
return SC_ERROR_OUT_OF_MEMORY;
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);
if (r < 0)
return r;
/* 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);
if (r < 0)
return r;
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);
if (r < 0)
return r;
/* 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;
return 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_pkcs15_cert_info *cert_info;
struct sc_pkcs15_object *object;
unsigned int usage;
const char *label;
int r;
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 */
if ((r = set_so_pin_from_card(p15card, profile)) < 0)
return r;
r = select_intrinsic_id(p15card, profile, SC_PKCS15_TYPE_CERT_X509, &args->id, &args->der_encoded);
if (r < 0)
return r;
/* Select an ID if the user didn't specify one, otherwise
* make sure it's unique */
if ((r = select_id(p15card, SC_PKCS15_TYPE_CERT, &args->id)) < 0)
return r;
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);
if (r < 0) {
sc_debug(p15card->card->ctx,
"Failed to assign user pin reference "
"(copied from private key auth_id)\n");
return r;
}
}
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)
return SC_ERROR_OUT_OF_MEMORY;
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;
return 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,
sc_pkcs15_object_t *object, sc_pkcs15_id_t *id,
sc_pkcs15_der_t *data,
sc_path_t *path)
{
struct sc_file *file = NULL;
int r;
unsigned int idx = -1;
/* Set the SO PIN reference from card */
if ((r = set_so_pin_from_card(p15card, profile)) < 0)
return r;
if (profile->ops->new_file == NULL) {
/* New API */
r = select_object_path(p15card, profile,
object, id,
path);
if (r < 0)
return r;
r = sc_profile_get_file_by_path(profile, path, &file);
if (r < 0)
return r;
} 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);
return 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;
}
Complete rewrite of OpenSC build system. 1. Build system now supports MinGW (Windows) compilation using msys and cross compilation. 2. Ability to explicitly disable and enable dependencies of the package. 3. openct, pcsc and nsplugins features are disabled by default. 4. Modified pcsc driver to use pcsc dynamically, no compile time dependency is required. 5. --enable-pcsc-lite configuration option renamed to --enable-pcsc. 6. Install opensc.conf file (as opensc.conf.new if opensc.conf exists). 7. Add--enable-doc configuration option, allow installing documentation into target. 8. Add --disable-man configuration option, allow msys mingw32 users to build from svn without extra dependencies. 9. Add export files to each library in order to export only required symbols. Windows native build may use these files instead of scanning objects' symbols. 10. Add opensc-tool --info to display some general information about the build. 11. Create compatibility library to be linked against library instread of recompiling the same source files in different places. 12. Add different win32 version resource to each class of outputs. 13. Make xsl-stylesheets location selectable. 14. Some win32 fixups. 15. Some warning fixups. 16. Many other autoconf/automake cleanups. Alon Bar-Lev svn diff -r 3315:3399 https://www.opensc-project.org/svn/opensc/branches/alonbl/mingw _M . D configure.in _M src _M src/openssh M src/openssh/Makefile.am _M src/tools M src/tools/rutoken-tool.c M src/tools/opensc-tool.c M src/tools/cardos-info.c M src/tools/pkcs15-crypt.c M src/tools/pkcs15-init.c M src/tools/piv-tool.c M src/tools/netkey-tool.c M src/tools/eidenv.c M src/tools/cryptoflex-tool.c M src/tools/util.c M src/tools/pkcs11-tool.c M src/tools/pkcs15-tool.c M src/tools/util.h M src/tools/opensc-explorer.c M src/tools/Makefile.am _M src/pkcs11 M src/pkcs11/pkcs11-global.c M src/pkcs11/framework-pkcs15.c M src/pkcs11/mechanism.c M src/pkcs11/pkcs11-display.c M src/pkcs11/pkcs11-object.c A src/pkcs11/opensc-pkcs11.exports M src/pkcs11/sc-pkcs11.h M src/pkcs11/pkcs11-spy.c M src/pkcs11/openssl.c M src/pkcs11/Makefile.am A src/pkcs11/pkcs11-spy.exports _M src/tests _M src/tests/regression M src/tests/regression/Makefile.am M src/tests/sc-test.c M src/tests/pintest.c M src/tests/Makefile.am _M src/include _M src/include/opensc M src/include/opensc/Makefile.am A src/include/opensc/svnignore M src/include/Makefile.am _M src/signer _M src/signer/npinclude M src/signer/npinclude/Makefile.am M src/signer/Makefile.am A src/signer/signer.exports _M src/common A src/common/compat_dummy.c D src/common/getopt.txt D src/common/strlcpy.c D src/common/LICENSE A src/common/compat_getopt.txt A src/common/compat_strlcpy.c A src/common/LICENSE.compat_getopt A src/common/compat_getopt.c D src/common/strlcpy.h D src/common/ChangeLog D src/common/getpass.c D src/common/my_getopt.c A src/common/compat_strlcpy.h A src/common/compat_getpass.c A src/common/compat_getopt.h A src/common/ChangeLog.compat_getopt D src/common/README.strlcpy D src/common/my_getopt.h A src/common/compat_getpass.h A src/common/README.compat_strlcpy D src/common/strlcpy.3 A src/common/README.compat_getopt D src/common/getopt.3 D src/common/README.my_getopt A src/common/compat_strlcpy.3 A src/common/compat_getopt.3 M src/common/Makefile.am M src/Makefile.am _M src/pkcs15init M src/pkcs15init/pkcs15-oberthur.c M src/pkcs15init/profile.c M src/pkcs15init/pkcs15-lib.c M src/pkcs15init/pkcs15-rutoken.c A src/pkcs15init/pkcs15init.exports M src/pkcs15init/pkcs15-gpk.c M src/pkcs15init/Makefile.am _M src/scconf M src/scconf/Makefile.am M src/scconf/parse.c A src/scconf/scconf.exports _M src/libopensc M src/libopensc/card-rutoken.c M src/libopensc/compression.c M src/libopensc/sc.c M src/libopensc/card-piv.c M src/libopensc/pkcs15-openpgp.c M src/libopensc/pkcs15-postecert.c M src/libopensc/pkcs15-tcos.c M src/libopensc/opensc-config.in M src/libopensc/reader-pcsc.c A src/libopensc/internal-winscard.h M src/libopensc/ctx.c A src/libopensc/libopensc.exports M src/libopensc/pkcs15-piv.c M src/libopensc/pkcs15-infocamere.c M src/libopensc/internal.h M src/libopensc/pkcs15-actalis.c M src/libopensc/pkcs15-starcert.c M src/libopensc/card-oberthur.c M src/libopensc/pkcs15-atrust-acos.c M src/libopensc/p15card-helper.c D src/libopensc/part10.h M src/libopensc/ui.c M src/libopensc/card-gpk.c M src/libopensc/pkcs15-wrap.c M src/libopensc/pkcs15-gemsafeGPK.c M src/libopensc/log.c M src/libopensc/pkcs15-esteid.c M src/libopensc/pkcs15-prkey-rutoken.c M src/libopensc/log.h M src/libopensc/Makefile.am M src/libopensc/reader-openct.c _M aclocal M aclocal/Makefile.am _M win32 M win32/Makefile.am A win32/versioninfo.rc.in A win32/ltrc.inc A configure.ac _M doc _M doc/tools M doc/tools/pkcs15-profile.xml D doc/changelog.sh D doc/export-wiki.xsl _M doc/api _M doc/api/file M doc/api/man.xsl _M doc/api/asn1 _M doc/api/apps _M doc/api/init _M doc/api/types _M doc/api/card M doc/api/html.xsl _M doc/api/misc _M doc/api/util M doc/Makefile.am D doc/export-wiki.sh AM doc/nonpersistent A doc/nonpersistent/export-wiki.xsl A doc/nonpersistent/Makefile.am A doc/nonpersistent/export-wiki.sh A doc/nonpersistent/svn2cl.xsl D doc/generate-man.sh D doc/svn2cl.xsl M Makefile.am A svnignore _M etc M etc/opensc.conf.in M etc/Makefile.am D man _M solaris M solaris/Makefile git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3405 c6295689-39f2-0310-b995-f0e70906c6a9
2008-03-06 16:06:59 +00:00
#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, 1);
#ifndef ENABLE_OPENSSL
SC_FUNC_RETURN(ctx, 1, 0);
#else
/* ID already exists */
if (id->len)
SC_FUNC_RETURN(ctx, 1, 0);
/* Native ID style is not an intrisic one */
if (profile->id_style == SC_PKCS15INIT_ID_STYLE_NATIVE)
SC_FUNC_RETURN(ctx, 1, 0);
/* 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, 1, 0);
}
/* 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, 1, 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 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)
{
sc_file_t *file;
const char *name;
int r;
char pbuf[SC_MAX_PATH_STRING_SIZE];
/* 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);
if (r < 0)
return r;
} else {
*path = profile->df_info->file->path;
}
/* If the profile specifies a key directory template,
* instantiate it now and create the DF
*/
switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) {
case SC_PKCS15_TYPE_PRKEY:
name = "private-key";
break;
case SC_PKCS15_TYPE_PUBKEY:
name = "public-key";
break;
case SC_PKCS15_TYPE_CERT:
name = "certificate";
break;
case SC_PKCS15_TYPE_DATA_OBJECT:
if (obj->flags & SC_PKCS15_CO_FLAG_PRIVATE)
name = "privdata";
else
name = "data";
break;
default:
return 0;
}
r = sc_path_print(pbuf, sizeof(pbuf), path);
if (r != SC_SUCCESS)
pbuf[0] = '\0';
sc_debug(p15card->card->ctx,
"key-domain.%s @%s (auth_id.len=%d)\n",
name, pbuf, obj->auth_id.len);
r = sc_profile_instantiate_template(profile,
"key-domain", path,
name, obj_id, &file);
if (r < 0) {
if (r == SC_ERROR_TEMPLATE_NOT_FOUND)
return 0;
return r;
}
*path = file->path;
sc_file_free(file);
return 0;
}
/*
* 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_card *card = p15card->card;
u8 *buf = NULL;
size_t size;
int r;
sc_debug(card->ctx, "called\n");
r = sc_pkcs15_encode_odf(card->ctx, p15card, &buf, &size);
if (r >= 0)
r = sc_pkcs15init_update_file(profile, card,
p15card->file_odf, buf, size);
if (buf)
free(buf);
return 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_pkcs15_df *df;
struct sc_card *card = p15card->card;
struct sc_file *file = NULL;
int is_new = 0, r = 0;
sc_debug(card->ctx, "called, DF %u obj %p\n", df_type, object);
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(card->ctx,
"Profile doesn't define a DF file %u",
df_type);
return SC_ERROR_NOT_SUPPORTED;
}
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);
if (r < 0)
return r;
} else {
/* Reused an existing object */
assert(object->df == df);
}
return sc_pkcs15init_update_any_df(p15card, profile, df, is_new);
#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_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;
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 (card->ctx->debug >= 2) {
sc_debug(card->ctx,
"no %s set for this card\n",
ident);
}
return 0;
}
sc_profile_get_pin_info(pro, pin_id, &pin_info);
type = SC_AC_CHV;
}
/* 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;
} else if (r == SC_ERROR_OBJECT_NOT_FOUND)
r = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED;
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 < 0)
return r;
/* 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);
if (r >= 0
&& (r = sc_verify(card, type, reference, pinbuf, *pinsize, NULL)) < 0) {
sc_debug(card->ctx, "Failed to verify %s (ref=0x%x)",
ident, reference);
}
}
return 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;
}