09af8714ad
* Avoid GCC 7 warnings with -Werror -Werror=implicit-fallthrough= libopensc/card-incrypto34.c not sure if this is a bug or intention libopensc/card-rutoken.c most probably intention libopensc/card-westcos.c remove bogus if so the compile is not confused I will fill a separate bug to gcc probably pkcs15init/pkcs15-iasecc.c Simplify the log and avoid compiler confusion sm/sm-common.c explicit fallthrough tools/pkcs11-tool.c use explicit fallthrough comment tools/pkcs15-init.c The fallthrough is obvious here -Werror=format-truncation= libopensc/pkcs15-itacns.c use explicit string lengths pkcs11/framework-pkcs15.c calculate the truncation tests/pintest.c avoid sprintf tools/pkcs15-crypt.c avoid sprintf tools/pkcs15-init.c calculate the truncation
3172 lines
80 KiB
C
3172 lines
80 KiB
C
/*
|
|
* Initialize Cards according to PKCS#15.
|
|
*
|
|
* This is a fill in the blanks sort of exercise. You need a
|
|
* profile that describes characteristics of your card, and the
|
|
* application specific layout on the card. This program will
|
|
* set up the card according to this specification (including
|
|
* PIN initialization etc) and create the corresponding PKCS15
|
|
* structure.
|
|
*
|
|
* There are a very few tasks that are too card specific to have
|
|
* a generic implementation; that is how PINs and keys are stored
|
|
* on the card. These should be implemented in pkcs15-<cardname>.c
|
|
*
|
|
* Copyright (C) 2002, Olaf Kirch <okir@suse.de>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
#include <assert.h>
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
#include <openssl/opensslv.h>
|
|
#include "libopensc/sc-ossl-compat.h"
|
|
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
|
|
#include <openssl/conf.h>
|
|
#endif
|
|
#include <openssl/evp.h>
|
|
#include <openssl/pem.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/rand.h>
|
|
#include <openssl/rsa.h>
|
|
#include <openssl/dsa.h>
|
|
#include <openssl/bn.h>
|
|
#include <openssl/pkcs12.h>
|
|
#include <openssl/x509v3.h>
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
|
|
#include <openssl/crypto.h>
|
|
#include <openssl/opensslconf.h> /* for OPENSSL_NO_EC */
|
|
#ifndef OPENSSL_NO_EC
|
|
#include <openssl/ec.h>
|
|
#endif /* OPENSSL_NO_EC */
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */
|
|
|
|
#include "common/compat_strlcpy.h"
|
|
#include "libopensc/internal.h"
|
|
#include "libopensc/cardctl.h"
|
|
#include "libopensc/pkcs15.h"
|
|
#include "libopensc/log.h"
|
|
#include "libopensc/cards.h"
|
|
#include "libopensc/asn1.h"
|
|
#include "pkcs15init/pkcs15-init.h"
|
|
#include "pkcs15init/profile.h"
|
|
#include "util.h"
|
|
|
|
#undef GET_KEY_ECHO_OFF
|
|
|
|
static const char *app_name = "pkcs15-init";
|
|
|
|
/* Handle encoding of PKCS15 on the card */
|
|
typedef int (*pkcs15_encoder)(sc_context_t *,
|
|
struct sc_pkcs15_card *, u8 **, size_t *);
|
|
|
|
/* Local functions */
|
|
static int open_reader_and_card(char *);
|
|
static int do_assert_pristine(sc_card_t *);
|
|
static int do_erase(sc_card_t *, struct sc_profile *);
|
|
static int do_erase_application(sc_card_t *, struct sc_profile *);
|
|
static int do_delete_objects(struct sc_profile *, unsigned int myopt_delete_flags);
|
|
static int do_change_attributes(struct sc_profile *, unsigned int myopt_type);
|
|
static int do_init_app(struct sc_profile *);
|
|
static int do_store_pin(struct sc_profile *);
|
|
static int do_generate_key(struct sc_profile *, const char *);
|
|
static int do_generate_skey(struct sc_profile *, const char *);
|
|
static int do_store_private_key(struct sc_profile *);
|
|
static int do_store_public_key(struct sc_profile *, EVP_PKEY *);
|
|
static int do_store_secret_key(struct sc_profile *);
|
|
static int do_store_certificate(struct sc_profile *);
|
|
static int do_update_certificate(struct sc_profile *);
|
|
static int do_convert_cert(sc_pkcs15_der_t *, X509 *);
|
|
static int is_cacert_already_present(struct sc_pkcs15init_certargs *);
|
|
static int do_finalize_card(sc_card_t *, struct sc_profile *);
|
|
|
|
static int do_read_data_object(const char *name, u8 **out, size_t *outlen, size_t expected);
|
|
static int do_store_data_object(struct sc_profile *profile);
|
|
static int do_sanity_check(struct sc_profile *profile);
|
|
|
|
static int init_prkeyargs(struct sc_pkcs15init_prkeyargs *);
|
|
static int init_skeyargs(struct sc_pkcs15init_skeyargs *);
|
|
static void init_gost_params(struct sc_pkcs15init_keyarg_gost_params *, EVP_PKEY *);
|
|
static int get_pin_callback(struct sc_profile *profile,
|
|
int id, const struct sc_pkcs15_auth_info *info,
|
|
const char *label,
|
|
u8 *pinbuf, size_t *pinsize);
|
|
static int get_key_callback(struct sc_profile *,
|
|
int method, int reference,
|
|
const u8 *, size_t, u8 *, size_t *);
|
|
|
|
static int do_read_private_key(const char *, const char *, EVP_PKEY **, X509 **, unsigned int);
|
|
static int do_read_public_key(const char *, const char *, EVP_PKEY **);
|
|
static int do_read_certificate(const char *, const char *, X509 **);
|
|
static char * cert_common_name(X509 *x509);
|
|
static void parse_commandline(int argc, char **argv);
|
|
static void read_options_file(const char *);
|
|
static void ossl_print_errors(void);
|
|
static int verify_pin(struct sc_pkcs15_card *, char *);
|
|
|
|
enum {
|
|
OPT_OPTIONS = 0x100,
|
|
OPT_PASSPHRASE,
|
|
OPT_PUBKEY,
|
|
OPT_SECRKEY,
|
|
OPT_EXTRACTABLE,
|
|
OPT_INSECURE,
|
|
OPT_AUTHORITY,
|
|
OPT_ASSERT_PRISTINE,
|
|
OPT_SECRET,
|
|
OPT_SECRKEY_ALGO,
|
|
OPT_PUBKEY_LABEL,
|
|
OPT_CERT_LABEL,
|
|
OPT_APPLICATION_NAME,
|
|
OPT_APPLICATION_ID,
|
|
OPT_PUK_ID,
|
|
OPT_PUK_LABEL,
|
|
OPT_VERIFY_PIN,
|
|
OPT_SANITY_CHECK,
|
|
OPT_BIND_TO_AID,
|
|
OPT_UPDATE_LAST_UPDATE,
|
|
OPT_ERASE_APPLICATION,
|
|
OPT_IGNORE_CA_CERTIFICATES,
|
|
OPT_UPDATE_EXISTING,
|
|
OPT_MD_CONTAINER_GUID,
|
|
OPT_VERSION,
|
|
|
|
OPT_PIN1 = 0x10000, /* don't touch these values */
|
|
OPT_PUK1 = 0x10001,
|
|
OPT_PIN2 = 0x10002,
|
|
OPT_PUK2 = 0x10003,
|
|
OPT_SERIAL = 0x10004,
|
|
OPT_NO_SOPIN = 0x10005,
|
|
OPT_USE_PINPAD= 0x10006,
|
|
OPT_USE_PINPAD_DEPRECATED
|
|
};
|
|
|
|
const struct option options[] = {
|
|
{ "version", 0, NULL, OPT_VERSION },
|
|
{ "erase-card", no_argument, NULL, 'E' },
|
|
{ "create-pkcs15", no_argument, NULL, 'C' },
|
|
{ "store-pin", no_argument, NULL, 'P' },
|
|
{ "generate-key", required_argument, NULL, 'G' },
|
|
{ "store-private-key", required_argument, NULL, 'S' },
|
|
{ "store-public-key", required_argument, NULL, OPT_PUBKEY },
|
|
{ "store-secret-key", required_argument, NULL, OPT_SECRKEY },
|
|
{ "store-certificate", required_argument, NULL, 'X' },
|
|
{ "update-certificate", required_argument, NULL, 'U' },
|
|
{ "store-data", required_argument, NULL, 'W' },
|
|
{ "delete-objects", required_argument, NULL, 'D' },
|
|
{ "change-attributes", required_argument, NULL, 'A' },
|
|
{ "sanity-check", no_argument, NULL, OPT_SANITY_CHECK},
|
|
{ "erase-application", required_argument, NULL, OPT_ERASE_APPLICATION},
|
|
|
|
{ "reader", required_argument, NULL, 'r' },
|
|
{ "pin", required_argument, NULL, OPT_PIN1 },
|
|
{ "puk", required_argument, NULL, OPT_PUK1 },
|
|
{ "so-pin", required_argument, NULL, OPT_PIN2 },
|
|
{ "so-puk", required_argument, NULL, OPT_PUK2 },
|
|
{ "no-so-pin", no_argument, NULL, OPT_NO_SOPIN },
|
|
{ "serial", required_argument, NULL, OPT_SERIAL },
|
|
{ "auth-id", required_argument, NULL, 'a' },
|
|
{ "puk-id", required_argument, NULL, OPT_PUK_ID },
|
|
{ "verify-pin", no_argument, NULL, OPT_VERIFY_PIN },
|
|
{ "id", required_argument, NULL, 'i' },
|
|
{ "label", required_argument, NULL, 'l' },
|
|
{ "puk-label", required_argument, NULL, OPT_PUK_LABEL },
|
|
{ "secret-key-algorithm", required_argument, NULL, OPT_SECRKEY_ALGO },
|
|
{ "public-key-label", required_argument, NULL, OPT_PUBKEY_LABEL },
|
|
{ "cert-label", required_argument, NULL, OPT_CERT_LABEL },
|
|
{ "application-name", required_argument, NULL, OPT_APPLICATION_NAME },
|
|
{ "application-id", required_argument, NULL, OPT_APPLICATION_ID },
|
|
{ "aid", required_argument, NULL, OPT_BIND_TO_AID },
|
|
{ "output-file", required_argument, NULL, 'o' },
|
|
{ "format", required_argument, NULL, 'f' },
|
|
{ "passphrase", required_argument, NULL, OPT_PASSPHRASE },
|
|
{ "authority", no_argument, NULL, OPT_AUTHORITY },
|
|
{ "key-usage", required_argument, NULL, 'u' },
|
|
{ "finalize", no_argument, NULL, 'F' },
|
|
{ "update-last-update", no_argument, NULL, OPT_UPDATE_LAST_UPDATE},
|
|
{ "ignore-ca-certificates",no_argument, NULL, OPT_IGNORE_CA_CERTIFICATES},
|
|
{ "update-existing", no_argument, NULL, OPT_UPDATE_EXISTING},
|
|
|
|
{ "extractable", no_argument, NULL, OPT_EXTRACTABLE },
|
|
{ "insecure", no_argument, NULL, OPT_INSECURE },
|
|
{ "use-default-transport-keys",
|
|
no_argument, NULL, 'T' },
|
|
{ "use-pinpad", no_argument, NULL, OPT_USE_PINPAD },
|
|
{ "no-prompt", no_argument, NULL, OPT_USE_PINPAD_DEPRECATED },
|
|
|
|
{ "profile", required_argument, NULL, 'p' },
|
|
{ "card-profile", required_argument, NULL, 'c' },
|
|
{ "options-file", required_argument, NULL, OPT_OPTIONS },
|
|
{ "md-container-guid", required_argument, NULL, OPT_MD_CONTAINER_GUID},
|
|
{ "wait", no_argument, NULL, 'w' },
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "verbose", no_argument, NULL, 'v' },
|
|
|
|
/* Hidden options for testing */
|
|
{ "assert-pristine", no_argument, NULL, OPT_ASSERT_PRISTINE },
|
|
{ "secret", required_argument, NULL, OPT_SECRET },
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
static const char * option_help[] = {
|
|
"Print OpenSC package version",
|
|
"Erase the smart card",
|
|
"Creates a new PKCS #15 structure",
|
|
"Store a new PIN/PUK on the card",
|
|
"Generate a new key and store it on the card",
|
|
"Store private key",
|
|
"Store public key",
|
|
"Store secret key",
|
|
"Store an X.509 certificate",
|
|
"Update an X.509 certificate (carefull with mail decryption certs!!)",
|
|
"Store a data object",
|
|
"Delete object(s) (use \"help\" for more information)",
|
|
"Change attribute(s) (use \"help\" for more information)",
|
|
"Card specific sanity check and possibly update procedure",
|
|
"Erase application with AID <arg>",
|
|
|
|
"Specify which reader to use",
|
|
"Specify PIN",
|
|
"Specify unblock PIN",
|
|
"Specify security officer (SO) PIN",
|
|
"Specify unblock PIN for SO PIN",
|
|
"Do not install a SO PIN, and do not prompt for it",
|
|
"Specify the serial number of the card",
|
|
"Specify ID of PIN to use/create",
|
|
"Specify ID of PUK to use/create",
|
|
"Verify PIN after card binding (use with --auth-id)",
|
|
"Specify ID of key/certificate",
|
|
"Specify label of PIN/key",
|
|
"Specify label of PUK",
|
|
"Specify secret key algorithm (use with --store-secret-key)",
|
|
"Specify public key label (use with --generate-key)",
|
|
"Specify user cert label (use with --store-private-key)",
|
|
"Specify application name of data object (use with --store-data-object)",
|
|
"Specify application id of data object (use with --store-data-object)",
|
|
"Specify AID of the on-card PKCS#15 application to be binded to (in hexadecimal form)",
|
|
"Output public portion of generated key to file",
|
|
"Specify key/cert file format: PEM (=default), DER or PKCS12",
|
|
"Specify passphrase for unlocking secret key",
|
|
"Mark certificate as a CA certificate",
|
|
"Specify X.509 key usage (use \"--key-usage help\" for more information)",
|
|
"Finish initialization phase of the smart card",
|
|
"Update 'lastUpdate' attribut of tokenInfo",
|
|
"When storing PKCS#12 ignore CA certificates",
|
|
"Store or update existing certificate",
|
|
|
|
"Private key stored as an extractable key",
|
|
"Insecure mode: do not require a PIN for private key",
|
|
"Do not ask for transport keys if the driver thinks it knows the key",
|
|
"Do not prompt the user; if no PINs supplied, pinpad will be used",
|
|
NULL,
|
|
|
|
"Specify the general profile to use",
|
|
"Specify the card profile to use",
|
|
"Read additional command line options from file",
|
|
"For a new key specify GUID for a MD container",
|
|
"Wait for card insertion",
|
|
"Display this message",
|
|
"Verbose operation. Use several times to enable debug output.",
|
|
|
|
NULL,
|
|
NULL,
|
|
};
|
|
|
|
enum {
|
|
ACTION_NONE = 0,
|
|
ACTION_ASSERT_PRISTINE,
|
|
ACTION_ERASE,
|
|
ACTION_INIT,
|
|
ACTION_DELETE_OBJECTS,
|
|
ACTION_STORE_PIN,
|
|
ACTION_GENERATE_KEY,
|
|
ACTION_STORE_PRIVKEY,
|
|
ACTION_STORE_PUBKEY,
|
|
ACTION_STORE_SECRKEY,
|
|
ACTION_STORE_CERT,
|
|
ACTION_UPDATE_CERT,
|
|
ACTION_STORE_DATA,
|
|
ACTION_FINALIZE_CARD,
|
|
ACTION_CHANGE_ATTRIBUTES,
|
|
ACTION_SANITY_CHECK,
|
|
ACTION_UPDATE_LAST_UPDATE,
|
|
ACTION_ERASE_APPLICATION,
|
|
ACTION_PRINT_VERSION,
|
|
|
|
ACTION_MAX
|
|
};
|
|
static const char *action_names[] = {
|
|
"do nothing",
|
|
"verify that card is pristine",
|
|
"erase card",
|
|
"create PKCS #15 meta structure",
|
|
"delete object(s)",
|
|
"store PIN",
|
|
"generate key",
|
|
"store private key",
|
|
"store public key",
|
|
"store secret key",
|
|
"store certificate",
|
|
"update certificate",
|
|
"store data object",
|
|
"finalizing card",
|
|
"change attribute(s)",
|
|
"check card's sanity",
|
|
"update 'last-update'",
|
|
"erase application"
|
|
};
|
|
|
|
#define MAX_CERTS 4
|
|
#define MAX_SECRETS 16
|
|
struct secret {
|
|
int type;
|
|
int reference;
|
|
sc_pkcs15_id_t id;
|
|
unsigned char key[64];
|
|
size_t len;
|
|
};
|
|
|
|
/* Flags for do_delete_crypto_objects() and do_change_attributes() */
|
|
#define SC_PKCS15INIT_TYPE_PRKEY 1
|
|
#define SC_PKCS15INIT_TYPE_PUBKEY 2
|
|
#define SC_PKCS15INIT_TYPE_CERT 4
|
|
#define SC_PKCS15INIT_TYPE_CHAIN (8 | 4)
|
|
#define SC_PKCS15INIT_TYPE_DATA 16
|
|
#define SC_PKCS15INIT_TYPE_SKEY 32
|
|
|
|
static sc_context_t * ctx = NULL;
|
|
static sc_card_t * card = NULL;
|
|
static struct sc_pkcs15_card * p15card = NULL;
|
|
static char * opt_reader = NULL;
|
|
static unsigned int opt_actions;
|
|
static int opt_extractable = 0,
|
|
opt_insecure = 0,
|
|
opt_authority = 0,
|
|
opt_use_pinpad = 0,
|
|
opt_no_sopin = 0,
|
|
opt_use_defkeys = 0,
|
|
opt_wait = 0,
|
|
opt_verify_pin = 0;
|
|
static const char * opt_profile = "pkcs15";
|
|
static char * opt_card_profile = NULL;
|
|
static char * opt_infile = NULL;
|
|
static char * opt_format = NULL;
|
|
static char * opt_authid = NULL;
|
|
static char * opt_objectid = NULL;
|
|
static char * opt_label = NULL;
|
|
static char * opt_puk_label = NULL;
|
|
static char * opt_pubkey_label = NULL;
|
|
static char * opt_secrkey_algo = NULL;
|
|
static char * opt_cert_label = NULL;
|
|
static const char * opt_pins[4];
|
|
static char * pins[4];
|
|
static char * opt_serial = NULL;
|
|
static const char * opt_passphrase = NULL;
|
|
static char * opt_newkey = NULL;
|
|
static char * opt_outkey = NULL;
|
|
static char * opt_application_id = NULL;
|
|
static char * opt_application_name = NULL;
|
|
static char * opt_bind_to_aid = NULL;
|
|
static char * opt_puk_authid = NULL;
|
|
static char * opt_md_container_guid = NULL;
|
|
static unsigned int opt_x509_usage = 0;
|
|
static unsigned int opt_delete_flags = 0;
|
|
static unsigned int opt_type = 0;
|
|
static int ignore_cmdline_pins = 0;
|
|
static struct secret opt_secrets[MAX_SECRETS];
|
|
static unsigned int opt_secret_count;
|
|
static int opt_ignore_ca_certs = 0;
|
|
static int opt_update_existing = 0;
|
|
static int verbose = 0;
|
|
|
|
static struct sc_pkcs15init_callbacks callbacks = {
|
|
get_pin_callback, /* get_pin() */
|
|
get_key_callback, /* get_key() */
|
|
};
|
|
|
|
/*
|
|
* Dialog types for get_pin
|
|
*/
|
|
#define SC_UI_USAGE_OTHER 0x0000
|
|
#define SC_UI_USAGE_NEW_PIN 0x0001
|
|
#define SC_UI_USAGE_UNBLOCK_PIN 0x0002
|
|
#define SC_UI_USAGE_CHANGE_PIN 0x0003
|
|
|
|
/*
|
|
* Dialog flags
|
|
*/
|
|
#define SC_UI_PIN_RETYPE 0x0001 /* new pin, retype */
|
|
#define SC_UI_PIN_OPTIONAL 0x0002 /* new pin optional */
|
|
#define SC_UI_PIN_CHECK_LENGTH 0x0004 /* check pin length */
|
|
#define SC_UI_PIN_MISMATCH_RETRY 0x0008 /* retry if new pin mismatch? */
|
|
|
|
/* Hints passed to get_pin
|
|
* M marks mandatory fields,
|
|
* O marks optional fields
|
|
*/
|
|
typedef struct sc_ui_hints {
|
|
const char * prompt; /* M: cmdline prompt */
|
|
const char * dialog_name; /* M: dialog name */
|
|
unsigned int usage; /* M: usage hint */
|
|
unsigned int flags; /* M: flags */
|
|
sc_card_t * card; /* M: card handle */
|
|
struct sc_pkcs15_card * p15card; /* O: pkcs15 handle */
|
|
|
|
/* We may not have a pkcs15 object yet when we get
|
|
* here, but we may have an idea of what it's going to
|
|
* look like. */
|
|
const char * obj_label; /* O: object (PIN) label */
|
|
union {
|
|
struct sc_pkcs15_auth_info *pin;
|
|
} info;
|
|
} sc_ui_hints_t;
|
|
|
|
/*
|
|
* ask user for a pin
|
|
*/
|
|
extern int get_pin(sc_ui_hints_t *hints, char **out);
|
|
static int get_new_pin(sc_ui_hints_t *, const char *, const char *,
|
|
char **);
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
struct sc_profile *profile = NULL;
|
|
unsigned int n;
|
|
int r = 0;
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x00907000L && OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
OPENSSL_config(NULL);
|
|
#endif
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !(defined LIBRESSL_VERSION_NUMBER)
|
|
/* Openssl 1.1.0 magic */
|
|
OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS
|
|
| OPENSSL_INIT_ADD_ALL_CIPHERS
|
|
| OPENSSL_INIT_ADD_ALL_DIGESTS
|
|
| OPENSSL_INIT_LOAD_CONFIG,
|
|
NULL);
|
|
#else
|
|
/* OpenSSL magic */
|
|
OpenSSL_add_all_algorithms();
|
|
OPENSSL_malloc_init();
|
|
#endif
|
|
|
|
#ifdef RANDOM_POOL
|
|
if (!RAND_load_file(RANDOM_POOL, 32))
|
|
util_fatal("Unable to seed random number pool for key generation");
|
|
#endif
|
|
|
|
parse_commandline(argc, argv);
|
|
|
|
if (optind != argc)
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
|
if (opt_actions == 0) {
|
|
fprintf(stderr, "No action specified.\n");
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
|
}
|
|
if (!opt_profile) {
|
|
fprintf(stderr, "No profile specified.\n");
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
|
}
|
|
|
|
/* Connect to the card */
|
|
if (!open_reader_and_card(opt_reader))
|
|
return 1;
|
|
|
|
sc_pkcs15init_set_callbacks(&callbacks);
|
|
|
|
/* Bind the card-specific operations and load the profile */
|
|
r = sc_pkcs15init_bind(card, opt_profile, opt_card_profile, NULL, &profile);
|
|
if (r < 0) {
|
|
printf("Couldn't bind to the card: %s\n", sc_strerror(r));
|
|
return 1;
|
|
}
|
|
|
|
for (n = 0; n < sizeof(pins)/sizeof(pins[0]); n++) {
|
|
pins[n] = NULL;
|
|
}
|
|
|
|
for (n = 0; n < ACTION_MAX; n++) {
|
|
unsigned int action = n;
|
|
|
|
if (!(opt_actions & (1 << action)))
|
|
continue;
|
|
|
|
if (action != ACTION_ERASE
|
|
&& action != ACTION_INIT
|
|
&& action != ACTION_ASSERT_PRISTINE
|
|
&& p15card == NULL) {
|
|
/* Read the PKCS15 structure from the card */
|
|
if (opt_bind_to_aid) {
|
|
struct sc_aid aid;
|
|
|
|
aid.len = sizeof(aid.value);
|
|
if (sc_hex_to_bin(opt_bind_to_aid, aid.value, &aid.len)) {
|
|
fprintf(stderr, "Invalid AID value: '%s'\n", opt_bind_to_aid);
|
|
return 1;
|
|
}
|
|
|
|
r = sc_pkcs15init_finalize_profile(card, profile, &aid);
|
|
if (r < 0) {
|
|
fprintf(stderr, "Finalize profile error %s\n", sc_strerror(r));
|
|
break;
|
|
}
|
|
|
|
r = sc_pkcs15_bind(card, &aid, &p15card);
|
|
}
|
|
else {
|
|
r = sc_pkcs15_bind(card, NULL, &p15card);
|
|
}
|
|
if (r) {
|
|
fprintf(stderr, "PKCS#15 binding failed: %s\n", sc_strerror(r));
|
|
break;
|
|
}
|
|
|
|
/* XXX: should compare card to profile here to make
|
|
* sure we're not messing things up */
|
|
|
|
if (verbose)
|
|
printf("Found %s\n", p15card->tokeninfo->label);
|
|
|
|
sc_pkcs15init_set_p15card(profile, p15card);
|
|
|
|
if (opt_verify_pin) {
|
|
r = verify_pin(p15card, opt_authid);
|
|
if (r) {
|
|
fprintf(stderr, "Failed to verify User PIN : %s\n",
|
|
sc_strerror(r));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (verbose && action != ACTION_ASSERT_PRISTINE)
|
|
printf("About to %s.\n", action_names[action]);
|
|
|
|
switch (action) {
|
|
case ACTION_PRINT_VERSION:
|
|
printf("%s\n", OPENSC_SCM_REVISION);
|
|
break;
|
|
case ACTION_ASSERT_PRISTINE:
|
|
/* skip printing error message */
|
|
if ((r = do_assert_pristine(card)) < 0)
|
|
goto out;
|
|
continue;
|
|
case ACTION_ERASE:
|
|
r = do_erase(card, profile);
|
|
break;
|
|
case ACTION_INIT:
|
|
r = do_init_app(profile);
|
|
break;
|
|
case ACTION_STORE_PIN:
|
|
r = do_store_pin(profile);
|
|
break;
|
|
case ACTION_STORE_PRIVKEY:
|
|
r = do_store_private_key(profile);
|
|
break;
|
|
case ACTION_STORE_PUBKEY:
|
|
r = do_store_public_key(profile, NULL);
|
|
break;
|
|
case ACTION_STORE_SECRKEY:
|
|
r = do_store_secret_key(profile);
|
|
break;
|
|
case ACTION_STORE_CERT:
|
|
r = do_store_certificate(profile);
|
|
break;
|
|
case ACTION_UPDATE_CERT:
|
|
r = do_update_certificate(profile);
|
|
break;
|
|
case ACTION_STORE_DATA:
|
|
r = do_store_data_object(profile);
|
|
break;
|
|
case ACTION_DELETE_OBJECTS:
|
|
r = do_delete_objects(profile, opt_delete_flags);
|
|
break;
|
|
case ACTION_CHANGE_ATTRIBUTES:
|
|
r = do_change_attributes(profile, opt_type);
|
|
break;
|
|
case ACTION_GENERATE_KEY:
|
|
r = do_generate_key(profile, opt_newkey);
|
|
if (r == SC_ERROR_INVALID_ARGUMENTS)
|
|
r = do_generate_skey(profile, opt_newkey);
|
|
break;
|
|
case ACTION_FINALIZE_CARD:
|
|
r = do_finalize_card(card, profile);
|
|
break;
|
|
case ACTION_SANITY_CHECK:
|
|
r = do_sanity_check(profile);
|
|
break;
|
|
case ACTION_UPDATE_LAST_UPDATE:
|
|
profile->dirty = 1;
|
|
break;
|
|
case ACTION_ERASE_APPLICATION:
|
|
r = do_erase_application(card, profile);
|
|
break;
|
|
default:
|
|
util_fatal("Action not yet implemented\n");
|
|
}
|
|
|
|
if (r < 0) {
|
|
fprintf(stderr, "Failed to %s: %s\n",
|
|
action_names[action], sc_strerror(r));
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (n = 0; n < sizeof(pins)/sizeof(pins[0]); n++) {
|
|
free(pins[n]);
|
|
}
|
|
|
|
out:
|
|
if (profile) {
|
|
sc_pkcs15init_unbind(profile);
|
|
}
|
|
if (p15card) {
|
|
sc_pkcs15_unbind(p15card);
|
|
}
|
|
if (card) {
|
|
sc_disconnect_card(card);
|
|
}
|
|
sc_release_context(ctx);
|
|
return r < 0? 1 : 0;
|
|
}
|
|
|
|
static int
|
|
open_reader_and_card(char *reader)
|
|
{
|
|
int r;
|
|
sc_context_param_t ctx_param;
|
|
|
|
memset(&ctx_param, 0, sizeof(ctx_param));
|
|
ctx_param.ver = 0;
|
|
ctx_param.app_name = app_name;
|
|
|
|
r = sc_context_create(&ctx, &ctx_param);
|
|
if (r) {
|
|
util_error("Failed to establish context: %s\n", sc_strerror(r));
|
|
return 0;
|
|
}
|
|
|
|
if (verbose > 1) {
|
|
ctx->debug = verbose;
|
|
sc_ctx_log_to_file(ctx, "stderr");
|
|
}
|
|
|
|
if (util_connect_card_ex(ctx, &card, reader, opt_wait, 0, verbose))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Make sure there's no pkcs15 structure on the card
|
|
*/
|
|
static int
|
|
do_assert_pristine(sc_card_t *in_card)
|
|
{
|
|
sc_path_t path;
|
|
int r, ok = 1;
|
|
|
|
r = sc_lock(in_card);
|
|
if (r < 0)
|
|
goto end;
|
|
|
|
sc_format_path("3F00", &path);
|
|
r = sc_select_file(in_card, &path, NULL);
|
|
if (r)
|
|
goto end;
|
|
|
|
sc_format_path("2F00", &path);
|
|
r = sc_select_file(in_card, &path, NULL);
|
|
if (r)
|
|
goto end;
|
|
|
|
/* For a while only the presence of OpenSC on-card pkcs#15 is checked.
|
|
TODO: Parse DIR(2F00) to get know if there is some PKCS#15 applications.*/
|
|
sc_format_path("5015", &path);
|
|
r = sc_select_file(in_card, &path, NULL);
|
|
if (r)
|
|
goto end;
|
|
|
|
ok = 0;
|
|
end:
|
|
sc_unlock(in_card);
|
|
if (!ok) {
|
|
fprintf(stderr,
|
|
"Card not pristine; detected (possibly incomplete) "
|
|
"PKCS#15 structure\n");
|
|
} else if (verbose) {
|
|
printf("Pristine card.\n");
|
|
}
|
|
|
|
return ok ? 0 : -1;
|
|
}
|
|
|
|
/* algorithm spec parsing */
|
|
struct alg_spec {
|
|
const char *spec;
|
|
int algorithm;
|
|
unsigned int keybits;
|
|
};
|
|
|
|
static const struct alg_spec alg_types_sym[] = {
|
|
{ "des", SC_ALGORITHM_DES, 56 },
|
|
{ "3des", SC_ALGORITHM_3DES, 192 },
|
|
{ "aes", SC_ALGORITHM_AES, 128 },
|
|
{ NULL, -1, 0 }
|
|
};
|
|
|
|
static const struct alg_spec alg_types_asym[] = {
|
|
{ "rsa", SC_ALGORITHM_RSA, 1024 },
|
|
{ "dsa", SC_ALGORITHM_DSA, 1024 },
|
|
{ "gost2001", SC_ALGORITHM_GOSTR3410, SC_PKCS15_GOSTR3410_KEYSIZE },
|
|
{ "ec", SC_ALGORITHM_EC, 0 },
|
|
{ NULL, -1, 0 }
|
|
};
|
|
|
|
static int
|
|
parse_alg_spec(const struct alg_spec *types, const char *spec, unsigned int *keybits, struct sc_pkcs15_prkey *prkey)
|
|
{
|
|
int i, algorithm = -1;
|
|
char *end;
|
|
|
|
for (i = 0; types[i].spec; i++) {
|
|
if (!strncasecmp(spec, types[i].spec, strlen(types[i].spec))) {
|
|
algorithm = types[i].algorithm;
|
|
*keybits = types[i].keybits;
|
|
spec += strlen(types[i].spec);
|
|
break;
|
|
}
|
|
}
|
|
if (algorithm < 0) {
|
|
util_error("Unknown algorithm \"%s\"", spec);
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
|
|
if (*spec == '/' || *spec == '-' || *spec == ':')
|
|
spec++;
|
|
|
|
if (*spec) {
|
|
if (isalpha(*spec) && algorithm == SC_ALGORITHM_EC && prkey) {
|
|
prkey->u.ec.params.named_curve = strdup(spec);
|
|
} else {
|
|
*keybits = strtoul(spec, &end, 10);
|
|
if (*end) {
|
|
util_error("Invalid number of key bits \"%s\"", spec);
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return algorithm;
|
|
}
|
|
|
|
/*
|
|
* Erase card
|
|
*/
|
|
static int
|
|
do_erase(sc_card_t *in_card, struct sc_profile *profile)
|
|
{
|
|
int r;
|
|
struct sc_pkcs15_card *p15card;
|
|
struct sc_aid aid;
|
|
struct sc_aid *paid = NULL;
|
|
|
|
p15card = sc_pkcs15_card_new();
|
|
p15card->card = in_card;
|
|
|
|
ignore_cmdline_pins++;
|
|
if (opt_bind_to_aid) {
|
|
aid.len = sizeof(aid.value);
|
|
r = sc_hex_to_bin(opt_bind_to_aid, aid.value, &aid.len);
|
|
if (r < 0) {
|
|
fprintf(stderr, "Invalid AID value: '%s'\n", opt_bind_to_aid);
|
|
goto err;
|
|
}
|
|
|
|
paid = &aid;
|
|
}
|
|
|
|
r = sc_lock(p15card->card);
|
|
if (r < 0)
|
|
goto err;
|
|
r = sc_pkcs15init_erase_card(p15card, profile, paid);
|
|
sc_unlock(p15card->card);
|
|
|
|
ignore_cmdline_pins--;
|
|
|
|
err:
|
|
sc_pkcs15_card_free(p15card);
|
|
return r;
|
|
}
|
|
|
|
static int
|
|
do_erase_application(sc_card_t *in_card, struct sc_profile *profile)
|
|
{
|
|
int r;
|
|
|
|
ignore_cmdline_pins--;
|
|
r = do_erase(in_card, profile);
|
|
ignore_cmdline_pins++;
|
|
return r;
|
|
}
|
|
|
|
static int do_finalize_card(sc_card_t *in_card, struct sc_profile *profile)
|
|
{
|
|
int r;
|
|
r = sc_lock(in_card);
|
|
if (r < 0)
|
|
return r;
|
|
r = sc_pkcs15init_finalize_card(in_card, profile);
|
|
sc_unlock(in_card);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Initialize pkcs15 application
|
|
*/
|
|
static int
|
|
do_init_app(struct sc_profile *profile)
|
|
{
|
|
struct sc_pkcs15init_initargs args;
|
|
sc_pkcs15_auth_info_t info;
|
|
sc_ui_hints_t hints;
|
|
const char *role = "so";
|
|
int r, so_puk_disabled = 0;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
memset(&info, 0, sizeof(info));
|
|
hints.usage = SC_UI_USAGE_NEW_PIN;
|
|
hints.flags = SC_UI_PIN_RETYPE
|
|
| SC_UI_PIN_CHECK_LENGTH
|
|
| SC_UI_PIN_MISMATCH_RETRY;
|
|
hints.card = card;
|
|
hints.p15card = NULL;
|
|
hints.info.pin = &info;
|
|
|
|
/* If it's the onepin option, we need the user PIN iso the SO PIN */
|
|
if (opt_profile && strstr(opt_profile, "+onepin")) {
|
|
if (opt_pins[0])
|
|
opt_pins[2] = opt_pins[0];
|
|
if (opt_pins[1])
|
|
opt_pins[3] = opt_pins[1];
|
|
}
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
|
|
sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &info);
|
|
|
|
if (!(info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN))
|
|
role = "user";
|
|
else
|
|
hints.flags |= SC_UI_PIN_OPTIONAL; /* SO PIN is always optional */
|
|
|
|
|
|
if ((info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED)
|
|
&& (info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN))
|
|
so_puk_disabled = 1;
|
|
|
|
|
|
if (!opt_pins[2] && !opt_use_pinpad && !opt_no_sopin) {
|
|
r = get_new_pin(&hints, role, "pin", &pins[2]);
|
|
if (r < 0)
|
|
goto failed;
|
|
opt_pins[2] = pins[2];
|
|
}
|
|
|
|
if (!so_puk_disabled && opt_pins[2] && !opt_pins[3] && !opt_use_pinpad) {
|
|
sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_SO_PUK, &info);
|
|
|
|
if (!(info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN))
|
|
role = "user";
|
|
|
|
hints.flags |= SC_UI_PIN_OPTIONAL;
|
|
r = get_new_pin(&hints, role, "puk", &pins[3]);
|
|
if (r < 0)
|
|
goto failed;
|
|
opt_pins[3] = pins[3];
|
|
}
|
|
|
|
args.so_pin = (const u8 *) opt_pins[2];
|
|
if (args.so_pin)
|
|
args.so_pin_len = strlen((const char *) args.so_pin);
|
|
|
|
if (!so_puk_disabled) {
|
|
args.so_puk = (const u8 *) opt_pins[3];
|
|
if (args.so_puk)
|
|
args.so_puk_len = strlen((const char *) args.so_puk);
|
|
}
|
|
|
|
args.serial = (const char *) opt_serial;
|
|
args.label = opt_label;
|
|
|
|
r = sc_lock(card);
|
|
if (r < 0)
|
|
return r;
|
|
r = sc_pkcs15init_add_app(card, profile, &args);
|
|
sc_unlock(card);
|
|
return r;
|
|
|
|
failed: fprintf(stderr, "Failed to read PIN: %s\n", sc_strerror(r));
|
|
return SC_ERROR_PKCS15INIT;
|
|
}
|
|
|
|
/*
|
|
* Store a PIN/PUK pair
|
|
*/
|
|
static int
|
|
do_store_pin(struct sc_profile *profile)
|
|
{
|
|
struct sc_pkcs15init_pinargs args;
|
|
sc_pkcs15_auth_info_t info;
|
|
sc_ui_hints_t hints;
|
|
int r;
|
|
const char *pin_id;
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.usage = SC_UI_USAGE_NEW_PIN;
|
|
hints.flags = SC_UI_PIN_RETYPE
|
|
| SC_UI_PIN_CHECK_LENGTH
|
|
| SC_UI_PIN_MISMATCH_RETRY;
|
|
hints.card = card;
|
|
hints.p15card = p15card;
|
|
hints.info.pin = &info;
|
|
|
|
pin_id = opt_objectid ? opt_objectid : opt_authid;
|
|
|
|
if (!pin_id) {
|
|
util_error("No pin id specified\n");
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
|
|
sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &info);
|
|
if (opt_pins[0] == NULL) {
|
|
if ((r = get_new_pin(&hints, "user", "pin", &pins[0])) < 0)
|
|
goto failed;
|
|
opt_pins[0] = pins[0];
|
|
}
|
|
|
|
if (*opt_pins[0] == '\0') {
|
|
util_error("You must specify a PIN\n");
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
sc_pkcs15_format_id(pin_id, &args.auth_id);
|
|
args.pin = (u8 *) opt_pins[0];
|
|
args.pin_len = strlen(opt_pins[0]);
|
|
args.label = opt_label;
|
|
|
|
if (!(info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED)
|
|
&& opt_pins[1] == NULL) {
|
|
sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &info);
|
|
|
|
hints.flags |= SC_UI_PIN_OPTIONAL;
|
|
if ((r = get_new_pin(&hints, "user", "puk", &pins[1])) < 0)
|
|
goto failed;
|
|
opt_pins[1] = pins[1];
|
|
}
|
|
|
|
if (opt_puk_authid && opt_pins[1])
|
|
sc_pkcs15_format_id(opt_puk_authid, &args.puk_id);
|
|
args.puk_label = opt_puk_label;
|
|
args.puk = (u8 *) opt_pins[1];
|
|
args.puk_len = opt_pins[1]? strlen(opt_pins[1]) : 0;
|
|
|
|
r = sc_lock(p15card->card);
|
|
if (r < 0)
|
|
return r;
|
|
r = sc_pkcs15init_store_pin(p15card, profile, &args);
|
|
sc_unlock(p15card->card);
|
|
return r;
|
|
|
|
failed: fprintf(stderr, "Failed to read PIN: %s\n", sc_strerror(r));
|
|
return SC_ERROR_PKCS15INIT;
|
|
}
|
|
|
|
/*
|
|
* Store a private key
|
|
*/
|
|
static int
|
|
do_store_private_key(struct sc_profile *profile)
|
|
{
|
|
struct sc_pkcs15init_prkeyargs args;
|
|
EVP_PKEY *pkey = NULL;
|
|
X509 *cert[MAX_CERTS];
|
|
int r, i, ncerts;
|
|
|
|
if ((r = init_prkeyargs(&args)) < 0)
|
|
return r;
|
|
|
|
r = do_read_private_key(opt_infile, opt_format, &pkey, cert, MAX_CERTS);
|
|
if (r < 0)
|
|
return r;
|
|
ncerts = r;
|
|
|
|
if (ncerts) {
|
|
char namebuf[256];
|
|
|
|
printf("Importing %d certificates:\n", opt_ignore_ca_certs ? 1 : ncerts);
|
|
for (i = 0; i < ncerts && !(i && opt_ignore_ca_certs); i++)
|
|
printf(" %d: %s\n", i, X509_NAME_oneline(X509_get_subject_name(cert[i]),
|
|
namebuf, sizeof(namebuf)));
|
|
}
|
|
|
|
r = sc_pkcs15_convert_prkey(&args.key, pkey);
|
|
if (r < 0)
|
|
return r;
|
|
init_gost_params(&args.params.gost, pkey);
|
|
|
|
if (ncerts) {
|
|
unsigned int usage;
|
|
|
|
/* tell openssl to cache the extensions */
|
|
X509_check_purpose(cert[0], -1, -1);
|
|
usage = X509_get_extended_key_usage(cert[0]);
|
|
|
|
/* No certificate usage? Assume ordinary
|
|
* user cert */
|
|
if (usage == 0)
|
|
usage = 0x1F;
|
|
|
|
/* If the user requested a specific key usage on the
|
|
* command line check if it includes _more_
|
|
* usage bits than the one specified by the cert,
|
|
* and complain if it does.
|
|
* If the usage specified on the command line
|
|
* is more restrictive, use that.
|
|
*/
|
|
if (~usage & opt_x509_usage) {
|
|
fprintf(stderr,
|
|
"Warning: requested key usage incompatible with "
|
|
"key usage specified by X.509 certificate\n");
|
|
}
|
|
|
|
args.x509_usage = opt_x509_usage? opt_x509_usage : usage;
|
|
}
|
|
|
|
args.access_flags |=
|
|
SC_PKCS15_PRKEY_ACCESS_SENSITIVE
|
|
| SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE
|
|
| SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE;
|
|
|
|
r = sc_lock(p15card->card);
|
|
if (r < 0)
|
|
return r;
|
|
r = sc_pkcs15init_store_private_key(p15card, profile, &args, NULL);
|
|
if (r < 0) {
|
|
sc_unlock(p15card->card);
|
|
return r;
|
|
}
|
|
|
|
/* If there are certificate as well (e.g. when reading the
|
|
* private key from a PKCS #12 file) store them, too.
|
|
*/
|
|
for (i = 0; i < ncerts && r >= 0; i++) {
|
|
struct sc_pkcs15init_certargs cargs;
|
|
char namebuf[SC_PKCS15_MAX_LABEL_SIZE-1];
|
|
|
|
if (i && opt_ignore_ca_certs)
|
|
break;
|
|
|
|
memset(&cargs, 0, sizeof(cargs));
|
|
|
|
/* Encode the cert */
|
|
if ((r = do_convert_cert(&cargs.der_encoded, cert[i])) < 0)
|
|
return r;
|
|
|
|
X509_check_purpose(cert[i], -1, -1);
|
|
cargs.x509_usage = X509_get_extended_key_usage(cert[i]);
|
|
|
|
cargs.label = cert_common_name(cert[i]);
|
|
if (!cargs.label)
|
|
cargs.label = X509_NAME_oneline(X509_get_subject_name(cert[i]), namebuf, sizeof(namebuf));
|
|
|
|
/* Just the first certificate gets the same ID
|
|
* as the private key. All others get
|
|
* an ID of their own */
|
|
if (i == 0) {
|
|
cargs.id = args.id;
|
|
if (opt_cert_label != 0)
|
|
cargs.label = opt_cert_label;
|
|
} else {
|
|
if (is_cacert_already_present(&cargs)) {
|
|
printf("Certificate #%d already present, not stored.\n", i);
|
|
goto next_cert;
|
|
}
|
|
cargs.authority = 1;
|
|
}
|
|
|
|
r = sc_pkcs15init_store_certificate(p15card, profile, &cargs, NULL);
|
|
next_cert:
|
|
free(cargs.der_encoded.value);
|
|
}
|
|
|
|
/* No certificates - store the public key */
|
|
if (ncerts == 0)
|
|
r = do_store_public_key(profile, pkey);
|
|
|
|
sc_unlock(p15card->card);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Check if the CA certificate is already present
|
|
*/
|
|
static int
|
|
is_cacert_already_present(struct sc_pkcs15init_certargs *args)
|
|
{
|
|
sc_pkcs15_object_t *objs[32];
|
|
sc_pkcs15_cert_info_t *cinfo;
|
|
sc_pkcs15_cert_t *cert;
|
|
int i, count, r;
|
|
|
|
r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32);
|
|
if (r <= 0)
|
|
return 0;
|
|
|
|
count = r;
|
|
for (i = 0; i < count; i++) {
|
|
cinfo = (sc_pkcs15_cert_info_t *) objs[i]->data;
|
|
|
|
if (!cinfo->authority)
|
|
continue;
|
|
if (strncmp(args->label, objs[i]->label, sizeof objs[i]->label))
|
|
continue;
|
|
/* XXX we should also match the usage field here */
|
|
|
|
/* Compare the DER representation of the certificates */
|
|
r = sc_pkcs15_read_certificate(p15card, cinfo, &cert);
|
|
if (r < 0 || !cert)
|
|
continue;
|
|
|
|
if (cert->data.len == args->der_encoded.len
|
|
&& !memcmp(cert->data.value, args->der_encoded.value, cert->data.len)) {
|
|
sc_pkcs15_free_certificate(cert);
|
|
return 1;
|
|
}
|
|
|
|
sc_pkcs15_free_certificate(cert);
|
|
cert=NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Store a public key
|
|
*/
|
|
static int
|
|
do_store_public_key(struct sc_profile *profile, EVP_PKEY *pkey)
|
|
{
|
|
struct sc_pkcs15init_pubkeyargs args;
|
|
sc_pkcs15_object_t *dummy;
|
|
int r = 0;
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
if (opt_objectid)
|
|
sc_pkcs15_format_id(opt_objectid, &args.id);
|
|
args.label = (opt_pubkey_label != 0 ? opt_pubkey_label : opt_label);
|
|
args.x509_usage = opt_x509_usage;
|
|
|
|
if (pkey == NULL) {
|
|
r = do_read_public_key(opt_infile, opt_format, &pkey);
|
|
}
|
|
if (r >= 0) {
|
|
r = sc_pkcs15_convert_pubkey(&args.key, pkey);
|
|
if (r >= 0)
|
|
init_gost_params(&args.params.gost, pkey);
|
|
}
|
|
if (r >= 0) {
|
|
r = sc_lock(p15card->card);
|
|
if (r < 0)
|
|
return r;
|
|
r = sc_pkcs15init_store_public_key(p15card, profile, &args, &dummy);
|
|
sc_unlock(p15card->card);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Store a secret key
|
|
*/
|
|
static int
|
|
do_store_secret_key(struct sc_profile *profile)
|
|
{
|
|
struct sc_pkcs15init_skeyargs args;
|
|
unsigned int keybits;
|
|
int r, algorithm = -1;
|
|
|
|
if (!opt_secrkey_algo) {
|
|
util_error("Specify secret key algorithm with --secret-key-algorithm");
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
|
|
if ((r = init_skeyargs(&args)) < 0)
|
|
return r;
|
|
|
|
algorithm = parse_alg_spec(alg_types_sym, opt_secrkey_algo, &keybits, 0);
|
|
if (algorithm < 0) {
|
|
util_error("Invalid symmetric key spec: \"%s\"", opt_secrkey_algo);
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
|
|
r = do_read_data_object(opt_infile, &args.key.data, &args.key.data_len, (keybits+7) / 8);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
args.algorithm = algorithm;
|
|
args.value_len = keybits;
|
|
args.access_flags |=
|
|
SC_PKCS15_PRKEY_ACCESS_SENSITIVE
|
|
| SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE
|
|
| SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE;
|
|
|
|
r = sc_lock(p15card->card);
|
|
if (r < 0)
|
|
return r;
|
|
r = sc_pkcs15init_store_secret_key(p15card, profile, &args, NULL);
|
|
sc_unlock(p15card->card);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Download certificate to card
|
|
*/
|
|
static int
|
|
do_store_certificate(struct sc_profile *profile)
|
|
{
|
|
struct sc_pkcs15init_certargs args;
|
|
X509 *cert = NULL;
|
|
int r;
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
|
|
if (opt_update_existing)
|
|
args.update = 1;
|
|
|
|
if (opt_objectid)
|
|
sc_pkcs15_format_id(opt_objectid, &args.id);
|
|
|
|
args.label = (opt_cert_label != 0 ? opt_cert_label : opt_label);
|
|
args.authority = opt_authority;
|
|
|
|
r = do_read_certificate(opt_infile, opt_format, &cert);
|
|
if (r >= 0)
|
|
r = do_convert_cert(&args.der_encoded, cert);
|
|
if (r >= 0) {
|
|
r = sc_lock(p15card->card);
|
|
if (r < 0)
|
|
return r;
|
|
r = sc_pkcs15init_store_certificate(p15card, profile, &args, NULL);
|
|
sc_unlock(p15card->card);
|
|
}
|
|
|
|
if (args.der_encoded.value)
|
|
free(args.der_encoded.value);
|
|
|
|
return r;
|
|
}
|
|
|
|
static int
|
|
do_read_check_certificate(sc_pkcs15_cert_t *sc_oldcert,
|
|
const char *filename, const char *format, sc_pkcs15_der_t *newcert_raw)
|
|
{
|
|
X509 *oldcert, *newcert;
|
|
EVP_PKEY *oldpk, *newpk;
|
|
int oldpk_type, newpk_type;
|
|
const u8 *ptr;
|
|
int r;
|
|
|
|
/* Get the public key from the old cert */
|
|
ptr = sc_oldcert->data.value;
|
|
oldcert = d2i_X509(NULL, &ptr, sc_oldcert->data.len);
|
|
|
|
if (oldcert == NULL)
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
/* Read the new cert from file and get it's public key */
|
|
r = do_read_certificate(filename, format, &newcert);
|
|
if (r < 0) {
|
|
X509_free(oldcert);
|
|
return r;
|
|
}
|
|
|
|
oldpk = X509_get_pubkey(oldcert);
|
|
newpk = X509_get_pubkey(newcert);
|
|
|
|
oldpk_type = EVP_PKEY_base_id(oldpk);
|
|
newpk_type = EVP_PKEY_base_id(newpk);
|
|
|
|
/* Compare the public keys, there's no high level openssl function for this(?) */
|
|
/* Yes there is in 1.0.2 and above EVP_PKEY_cmp */
|
|
|
|
|
|
r = SC_ERROR_INVALID_ARGUMENTS;
|
|
if (oldpk_type == newpk_type)
|
|
{
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
|
if (EVP_PKEY_cmp(oldpk, newpk) == 1)
|
|
r = 0;
|
|
#else
|
|
if ((oldpk_type == EVP_PKEY_DSA) &&
|
|
!BN_cmp(EVP_PKEY_get0_DSA(oldpk)->p, EVP_PKEY_get0_DSA(newpk)->p) &&
|
|
!BN_cmp(EVP_PKEY_get0_DSA(oldpk)->q, EVP_PKEY_get0_DSA(newpk)->q) &&
|
|
!BN_cmp(EVP_PKEY_get0_DSA(oldpk)->g, EVP_PKEY_get0_DSA(newpk)->g))
|
|
r = 0;
|
|
else if ((oldpk_type == EVP_PKEY_RSA) &&
|
|
!BN_cmp(EVP_PKEY_get0_RSA(oldpk)->n, EVP_PKEY_get0_RSA(newpk)->n) &&
|
|
!BN_cmp(EVP_PKEY_get0_RSA(oldpk)->e, EVP_PKEY_get0_RSA(newpk)->e))
|
|
r = 0;
|
|
#endif
|
|
}
|
|
|
|
EVP_PKEY_free(newpk);
|
|
EVP_PKEY_free(oldpk);
|
|
X509_free(oldcert);
|
|
|
|
if (r == 0)
|
|
r = do_convert_cert(newcert_raw, newcert);
|
|
else
|
|
util_error("the public keys in the old and new certificate differ");
|
|
|
|
X509_free(newcert);
|
|
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Update an existing certificate with a new certificate having
|
|
* the same public key.
|
|
*/
|
|
static int
|
|
do_update_certificate(struct sc_profile *profile)
|
|
{
|
|
sc_pkcs15_id_t id;
|
|
sc_pkcs15_object_t *obj;
|
|
sc_pkcs15_cert_info_t *certinfo;
|
|
sc_pkcs15_cert_t *oldcert = NULL;
|
|
sc_pkcs15_der_t newcert_raw;
|
|
int r;
|
|
|
|
if (opt_objectid == NULL) {
|
|
util_error("no ID given for the cert: use --id");
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
|
|
sc_pkcs15_format_id(opt_objectid, &id);
|
|
|
|
if (sc_pkcs15_find_cert_by_id(p15card, &id, &obj) != 0) {
|
|
util_error("Couldn't find the cert with ID %s\n", opt_objectid);
|
|
return SC_ERROR_OBJECT_NOT_FOUND;
|
|
}
|
|
|
|
r = sc_lock(p15card->card);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
certinfo = (sc_pkcs15_cert_info_t *) obj->data;
|
|
r = sc_pkcs15_read_certificate(p15card, certinfo, &oldcert);
|
|
if (r < 0)
|
|
goto err;
|
|
|
|
newcert_raw.value = NULL;
|
|
r = do_read_check_certificate(oldcert, opt_infile, opt_format, &newcert_raw);
|
|
sc_pkcs15_free_certificate(oldcert);
|
|
if (r < 0)
|
|
goto err;
|
|
|
|
r = sc_pkcs15init_update_certificate(p15card, profile, obj,
|
|
newcert_raw.value, newcert_raw.len);
|
|
|
|
if (newcert_raw.value)
|
|
free(newcert_raw.value);
|
|
|
|
err:
|
|
sc_unlock(p15card->card);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Download data object to card
|
|
*/
|
|
static int
|
|
do_store_data_object(struct sc_profile *profile)
|
|
{
|
|
struct sc_pkcs15init_dataargs args;
|
|
unsigned char *data = NULL;
|
|
size_t datalen;
|
|
int r=0;
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
sc_init_oid(&args.app_oid);
|
|
|
|
if (opt_objectid)
|
|
sc_pkcs15_format_id(opt_objectid, &args.id);
|
|
if (opt_authid)
|
|
sc_pkcs15_format_id(opt_authid, &args.auth_id);
|
|
args.label = opt_label;
|
|
args.app_label = opt_application_name ? opt_application_name : "pkcs15-init";
|
|
|
|
sc_format_oid(&args.app_oid, opt_application_id);
|
|
if (opt_application_id && (args.app_oid.value[0] == -1)) {
|
|
util_error("Invalid OID \"%s\"", opt_application_id);
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
|
|
r = do_read_data_object(opt_infile, &data, &datalen, 0);
|
|
if (r >= 0) {
|
|
/* der_encoded contains the plain data, nothing DER encoded */
|
|
args.der_encoded.value = data;
|
|
args.der_encoded.len = datalen;
|
|
r = sc_lock(p15card->card);
|
|
if (r < 0) {
|
|
free(data);
|
|
return r;
|
|
}
|
|
r = sc_pkcs15init_store_data_object(p15card, profile, &args, NULL);
|
|
sc_unlock(p15card->card);
|
|
}
|
|
|
|
if (data)
|
|
free(data);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Run card specific sanity check procedure
|
|
*/
|
|
static int
|
|
do_sanity_check(struct sc_profile *profile)
|
|
{
|
|
int r;
|
|
r = sc_lock(p15card->card);
|
|
if (r < 0)
|
|
return r;
|
|
r = sc_pkcs15init_sanity_check(p15card, profile);
|
|
sc_unlock(p15card->card);
|
|
return r;
|
|
}
|
|
|
|
static int cert_is_root(sc_pkcs15_cert_t *c)
|
|
{
|
|
return (c->subject_len == c->issuer_len) &&
|
|
(memcmp(c->subject, c->issuer, c->subject_len) == 0);
|
|
}
|
|
|
|
/* Check if the cert has a 'sibling' and return it's parent cert.
|
|
* Should be made more effcicient for long chains by caching the certs.
|
|
*/
|
|
static int get_cert_info(sc_pkcs15_card_t *myp15card, sc_pkcs15_object_t *certobj,
|
|
int *has_sibling, int *stop, sc_pkcs15_object_t **issuercert)
|
|
{
|
|
sc_pkcs15_cert_t *cert = NULL;
|
|
sc_pkcs15_object_t *otherobj;
|
|
sc_pkcs15_cert_t *othercert = NULL;
|
|
int r;
|
|
|
|
*issuercert = NULL;
|
|
*has_sibling = 0;
|
|
*stop = 0;
|
|
|
|
r = sc_pkcs15_read_certificate(myp15card, (sc_pkcs15_cert_info_t *) certobj->data, &cert);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (cert_is_root(cert)) {
|
|
*stop = 1; /* root -> no parent and hence no siblings */
|
|
goto done;
|
|
}
|
|
for (otherobj = myp15card->obj_list; otherobj != NULL; otherobj = otherobj->next) {
|
|
if ((otherobj == certobj) ||
|
|
!((otherobj->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT))
|
|
continue;
|
|
if (othercert) {
|
|
sc_pkcs15_free_certificate(othercert);
|
|
othercert=NULL;
|
|
}
|
|
r = sc_pkcs15_read_certificate(myp15card, (sc_pkcs15_cert_info_t *) otherobj->data, &othercert);
|
|
if (r < 0 || !othercert)
|
|
goto done;
|
|
if ((cert->issuer_len == othercert->subject_len) &&
|
|
(memcmp(cert->issuer, othercert->subject, cert->issuer_len) == 0)) {
|
|
/* parent cert found */
|
|
*issuercert = otherobj;
|
|
*stop = cert_is_root(othercert);
|
|
}
|
|
else if (!cert_is_root(othercert) && (cert->issuer_len == othercert->issuer_len) &&
|
|
(memcmp(cert->issuer, othercert->issuer, cert->issuer_len) == 0)) {
|
|
*has_sibling = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
done:
|
|
sc_pkcs15_free_certificate(cert);
|
|
if (othercert)
|
|
sc_pkcs15_free_certificate(othercert);
|
|
|
|
return r;
|
|
}
|
|
|
|
/* Delete object(s) by ID. The 'which' param can be any combination of
|
|
* SC_PKCS15INIT_TYPE_PRKEY, SC_PKCS15INIT_TYPE_PUBKEY, SC_PKCS15INIT_TYPE_CERT
|
|
* and SC_PKCS15INIT_TYPE_CHAIN. In the last case, every cert in the chain is
|
|
* deleted, starting with the cert with ID 'id' and untill a CA cert is
|
|
* reached that certified other remaining certs on the card.
|
|
*/
|
|
static int do_delete_crypto_objects(sc_pkcs15_card_t *myp15card,
|
|
struct sc_profile *profile,
|
|
const sc_pkcs15_id_t *id,
|
|
unsigned int which)
|
|
{
|
|
sc_pkcs15_object_t *objs[10]; /* 1 priv + 1 pub + chain of at most 8 certs, should be enough */
|
|
int i, r = 0, count = 0, del_cert = 0;
|
|
|
|
if (which & SC_PKCS15INIT_TYPE_PRKEY) {
|
|
sc_pkcs15_object_t *key_objs[0x10];
|
|
|
|
r = sc_pkcs15_get_objects(myp15card, SC_PKCS15_TYPE_PRKEY, key_objs, 0x10);
|
|
if (r < 0) {
|
|
fprintf(stderr, "Private key enumeration failed: %s\n", sc_strerror(r));
|
|
return r;
|
|
}
|
|
|
|
for (i = 0; i< r; i++)
|
|
if (sc_pkcs15_compare_id(id, &((struct sc_pkcs15_prkey_info *)key_objs[i]->data)->id))
|
|
objs[count++] = key_objs[i];
|
|
|
|
if (!count)
|
|
fprintf(stderr, "NOTE: couldn't find privkey %s to delete\n", sc_pkcs15_print_id(id));
|
|
}
|
|
|
|
if (which & SC_PKCS15INIT_TYPE_PUBKEY) {
|
|
if (sc_pkcs15_find_pubkey_by_id(myp15card, id, &objs[count]) != 0)
|
|
fprintf(stderr, "NOTE: couldn't find pubkey %s to delete\n", sc_pkcs15_print_id(id));
|
|
else
|
|
count++;
|
|
}
|
|
|
|
if (which & SC_PKCS15INIT_TYPE_CERT) {
|
|
if (sc_pkcs15_find_cert_by_id(myp15card, id, &objs[count]) != 0)
|
|
fprintf(stderr, "NOTE: couldn't find cert %s to delete\n", sc_pkcs15_print_id(id));
|
|
else {
|
|
count++;
|
|
del_cert = 1;
|
|
}
|
|
}
|
|
|
|
if (which & SC_PKCS15INIT_TYPE_SKEY) {
|
|
if (sc_pkcs15_find_skey_by_id(myp15card, id, &objs[count]) != 0)
|
|
fprintf(stderr, "NOTE: couldn't find secrkey %s to delete\n", sc_pkcs15_print_id(id));
|
|
else
|
|
count++;
|
|
}
|
|
|
|
if (del_cert && ((which & SC_PKCS15INIT_TYPE_CHAIN) == SC_PKCS15INIT_TYPE_CHAIN)) {
|
|
/* Get the cert chain, stop if there's a CA that is the issuer of
|
|
* other certs on this card */
|
|
int has_sibling; /* siblings: certs having the same issuer */
|
|
int stop;
|
|
for( ; count < 10 ; count++) {
|
|
r = get_cert_info(myp15card, objs[count - 1], &has_sibling, &stop, &objs[count]);
|
|
if (r < 0)
|
|
fprintf(stderr, "get_cert_info() failed: %s\n", sc_strerror(r));
|
|
else if (has_sibling)
|
|
fprintf(stderr, "Chain deletion stops with cert %s\n", sc_pkcs15_print_id(
|
|
&((sc_pkcs15_cert_info_t *) objs[count - 1]->data)->id));
|
|
else if (stop && (objs[count] != NULL))
|
|
count++;
|
|
if (stop || (objs[count] == NULL))
|
|
break;
|
|
}
|
|
if (r < 0)
|
|
count = -1; /* Something wrong -> don't delete anything */
|
|
}
|
|
|
|
for (i = 0; i < count; i++) {
|
|
r = sc_pkcs15init_delete_object(myp15card, profile, objs[i]);
|
|
if (r < 0) {
|
|
fprintf(stderr, "Failed to delete object %d: %s\n", i, sc_strerror(r));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return r < 0 ? r : count;
|
|
}
|
|
|
|
static int
|
|
do_delete_objects(struct sc_profile *profile, unsigned int myopt_delete_flags)
|
|
{
|
|
int r = 0, count = 0;
|
|
|
|
r = sc_lock(p15card->card);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (myopt_delete_flags & SC_PKCS15INIT_TYPE_DATA) {
|
|
struct sc_object_id app_oid;
|
|
sc_pkcs15_object_t *obj = NULL;
|
|
|
|
if (opt_application_id != NULL) {
|
|
sc_format_oid(&app_oid, opt_application_id);
|
|
|
|
r = sc_pkcs15_find_data_object_by_app_oid(p15card, &app_oid, &obj);
|
|
}
|
|
else if (opt_application_name != NULL && opt_label != NULL) {
|
|
r = sc_pkcs15_find_data_object_by_name(p15card, opt_application_name, opt_label, &obj);
|
|
}
|
|
else {
|
|
util_fatal("Specify the --application-id or --application-name and --label for the data object to be deleted\n");
|
|
}
|
|
|
|
if (r >= 0) {
|
|
r = sc_pkcs15init_delete_object(p15card, profile, obj);
|
|
if (r >= 0)
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if (myopt_delete_flags & (SC_PKCS15INIT_TYPE_PRKEY | SC_PKCS15INIT_TYPE_PUBKEY | SC_PKCS15INIT_TYPE_CHAIN | SC_PKCS15INIT_TYPE_SKEY)) {
|
|
sc_pkcs15_id_t id;
|
|
if (opt_objectid == NULL)
|
|
util_fatal("Specify the --id for key(s) or cert(s) to be deleted\n");
|
|
sc_pkcs15_format_id(opt_objectid, &id);
|
|
|
|
r = do_delete_crypto_objects(p15card, profile, &id, myopt_delete_flags);
|
|
if (r >= 0)
|
|
count += r;
|
|
}
|
|
|
|
sc_unlock(p15card->card);
|
|
printf("Deleted %d objects\n", count);
|
|
|
|
return r;
|
|
}
|
|
|
|
static int
|
|
do_change_attributes(struct sc_profile *profile, unsigned int myopt_type)
|
|
{
|
|
int r = 0;
|
|
sc_pkcs15_id_t id;
|
|
sc_pkcs15_object_t *obj = NULL;
|
|
|
|
if (opt_objectid == NULL) {
|
|
printf("You have to specify the --id of the object\n");
|
|
return 0;
|
|
}
|
|
sc_pkcs15_format_id(opt_objectid, &id);
|
|
|
|
/* Right now, only changing the label is supported */
|
|
if (opt_label == NULL) {
|
|
printf("You should specify a --label\n");
|
|
return 0;
|
|
}
|
|
|
|
switch(myopt_type) {
|
|
case SC_PKCS15INIT_TYPE_PRKEY:
|
|
if ((r = sc_pkcs15_find_prkey_by_id(p15card, &id, &obj)) != 0)
|
|
return r;
|
|
break;
|
|
case SC_PKCS15INIT_TYPE_PUBKEY:
|
|
if ((r = sc_pkcs15_find_pubkey_by_id(p15card, &id, &obj)) != 0)
|
|
return r;
|
|
break;
|
|
case SC_PKCS15INIT_TYPE_CERT:
|
|
if ((r = sc_pkcs15_find_cert_by_id(p15card, &id, &obj)) != 0)
|
|
return r;
|
|
break;
|
|
case SC_PKCS15INIT_TYPE_DATA:
|
|
if ((r = sc_pkcs15_find_data_object_by_id(p15card, &id, &obj)) != 0)
|
|
return r;
|
|
break;
|
|
case SC_PKCS15INIT_TYPE_SKEY:
|
|
if ((r = sc_pkcs15_find_skey_by_id(p15card, &id, &obj)) != 0)
|
|
return r;
|
|
break;
|
|
}
|
|
|
|
if (obj == NULL) {
|
|
printf("No object of the specified type and with id = \"%s\" found\n", opt_objectid);
|
|
return 0;
|
|
}
|
|
|
|
if (opt_label != NULL) {
|
|
strlcpy(obj->label, opt_label, sizeof(obj->label));
|
|
}
|
|
|
|
r = sc_lock(p15card->card);
|
|
if (r < 0)
|
|
return r;
|
|
r = sc_pkcs15init_update_any_df(p15card, profile, obj->df, 0);
|
|
sc_unlock(p15card->card);
|
|
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Generate a new private key
|
|
*/
|
|
static int
|
|
do_generate_key(struct sc_profile *profile, const char *spec)
|
|
{
|
|
struct sc_pkcs15init_keygen_args keygen_args;
|
|
unsigned int keybits = 0;
|
|
int r, algorithm = -1;
|
|
|
|
memset(&keygen_args, 0, sizeof(keygen_args));
|
|
keygen_args.pubkey_label = opt_pubkey_label;
|
|
|
|
if ((r = init_prkeyargs(&keygen_args.prkey_args)) < 0)
|
|
return r;
|
|
|
|
algorithm = parse_alg_spec(alg_types_asym, spec, &keybits, &keygen_args.prkey_args.key);
|
|
if (algorithm < 0) {
|
|
util_error("Invalid key spec: \"%s\"", spec);
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
|
|
keygen_args.prkey_args.key.algorithm = algorithm;
|
|
keygen_args.prkey_args.access_flags |=
|
|
SC_PKCS15_PRKEY_ACCESS_SENSITIVE
|
|
| SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE
|
|
| SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE
|
|
| SC_PKCS15_PRKEY_ACCESS_LOCAL;
|
|
|
|
switch (algorithm) {
|
|
case SC_ALGORITHM_GOSTR3410:
|
|
/* FIXME: now only SC_PKCS15_PARAMSET_GOSTR3410_A */
|
|
keygen_args.prkey_args.params.gost.gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_A;
|
|
break;
|
|
}
|
|
|
|
r = sc_lock(p15card->card);
|
|
if (r == 0)
|
|
r = sc_pkcs15init_generate_key(p15card, profile, &keygen_args, keybits, NULL);
|
|
sc_unlock(p15card->card);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Generate a new secret key
|
|
*/
|
|
static int
|
|
do_generate_skey(struct sc_profile *profile, const char *spec)
|
|
{
|
|
struct sc_pkcs15init_skeyargs skey_args;
|
|
unsigned int keybits;
|
|
int r, algorithm = -1;
|
|
|
|
if ((r = init_skeyargs(&skey_args)) < 0)
|
|
return r;
|
|
skey_args.algorithm = algorithm;
|
|
|
|
algorithm = parse_alg_spec(alg_types_sym, spec, &keybits, 0);
|
|
if (algorithm < 0) {
|
|
util_error("Invalid symmetric key spec: \"%s\"", spec);
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
skey_args.value_len = keybits;
|
|
|
|
r = sc_lock(p15card->card);
|
|
if (r == 0)
|
|
r = sc_pkcs15init_generate_secret_key(p15card, profile, &skey_args, NULL);
|
|
sc_unlock(p15card->card);
|
|
return r;
|
|
}
|
|
|
|
static int init_prkeyargs(struct sc_pkcs15init_prkeyargs *args)
|
|
{
|
|
memset(args, 0, sizeof(*args));
|
|
if (opt_objectid)
|
|
sc_pkcs15_format_id(opt_objectid, &args->id);
|
|
if (opt_authid) {
|
|
sc_pkcs15_format_id(opt_authid, &args->auth_id);
|
|
} else if (!opt_insecure) {
|
|
util_error("no PIN given for key - either use --insecure or \n"
|
|
"specify a PIN using --auth-id");
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
if (opt_extractable) {
|
|
args->access_flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE;
|
|
}
|
|
args->label = opt_label;
|
|
args->x509_usage = opt_x509_usage;
|
|
|
|
if (opt_md_container_guid) {
|
|
args->guid = (unsigned char *)opt_md_container_guid;
|
|
args->guid_len = strlen(opt_md_container_guid);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int init_skeyargs(struct sc_pkcs15init_skeyargs *args)
|
|
{
|
|
memset(args, 0, sizeof(*args));
|
|
if (opt_objectid)
|
|
sc_pkcs15_format_id(opt_objectid, &args->id);
|
|
if (opt_authid) {
|
|
sc_pkcs15_format_id(opt_authid, &args->auth_id);
|
|
} else if (!opt_insecure) {
|
|
util_error("no PIN given for key - either use --insecure or \n"
|
|
"specify a PIN using --auth-id");
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
}
|
|
if (opt_extractable) {
|
|
args->access_flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE;
|
|
}
|
|
args->label = opt_label;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
init_gost_params(struct sc_pkcs15init_keyarg_gost_params *params, EVP_PKEY *pkey)
|
|
{
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC)
|
|
EC_KEY *key;
|
|
|
|
assert(pkey);
|
|
if (EVP_PKEY_id(pkey) == NID_id_GostR3410_2001) {
|
|
key = EVP_PKEY_get0(pkey);
|
|
assert(key);
|
|
assert(params);
|
|
assert(EC_KEY_get0_group(key));
|
|
assert(EC_GROUP_get_curve_name(EC_KEY_get0_group(key)) > 0);
|
|
switch (EC_GROUP_get_curve_name(EC_KEY_get0_group(key))) {
|
|
case NID_id_GostR3410_2001_CryptoPro_A_ParamSet:
|
|
params->gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_A;
|
|
break;
|
|
case NID_id_GostR3410_2001_CryptoPro_B_ParamSet:
|
|
params->gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_B;
|
|
break;
|
|
case NID_id_GostR3410_2001_CryptoPro_C_ParamSet:
|
|
params->gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_C;
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
(void)params, (void)pkey; /* no warning */
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Intern secrets given on the command line (mostly for testing)
|
|
*/
|
|
static void
|
|
parse_secret(struct secret *secret, const char *arg)
|
|
{
|
|
char *copy, *str, *value;
|
|
size_t len;
|
|
|
|
str = copy = strdup(arg);
|
|
if (!(value = strchr(str, '=')))
|
|
goto parse_err;
|
|
*value++ = '\0';
|
|
|
|
if (*str == '@') {
|
|
sc_pkcs15_format_id(str+1, &secret->id);
|
|
secret->type = SC_AC_CHV;
|
|
secret->reference = -1;
|
|
} else {
|
|
if (strncasecmp(str, "chv", 3))
|
|
secret->type = SC_AC_CHV;
|
|
else if (strncasecmp(str, "aut", 3))
|
|
secret->type = SC_AC_AUT;
|
|
else if (strncasecmp(str, "pro", 3))
|
|
secret->type = SC_AC_PRO;
|
|
else
|
|
goto parse_err;
|
|
str += 3;
|
|
if (!isdigit(str[3]))
|
|
goto parse_err;
|
|
secret->reference = strtoul(str, &str, 10);
|
|
if (*str != '\0')
|
|
goto parse_err;
|
|
}
|
|
if ((len = strlen(value)) < 3 || value[2] != ':') {
|
|
memcpy(secret->key, value, len);
|
|
} else {
|
|
len = sizeof(secret->key);
|
|
if (sc_hex_to_bin(value, secret->key, &len) < 0)
|
|
goto parse_err;
|
|
}
|
|
secret->len = len;
|
|
free(copy);
|
|
return;
|
|
|
|
parse_err:
|
|
util_fatal("Cannot parse secret \"%s\"\n", arg);
|
|
}
|
|
|
|
|
|
/*
|
|
* Prompt for a new PIN
|
|
* @role can be "user" or "so"
|
|
* @usage can be "pin" or "puk"
|
|
*/
|
|
static int get_new_pin(sc_ui_hints_t *hints,
|
|
const char *role, const char *usage,
|
|
char **retstr)
|
|
{
|
|
char name[32], prompt[64], label[64];
|
|
|
|
snprintf(name, sizeof(name), "pkcs15init.new_%s_%s", role, usage);
|
|
|
|
if (!strcmp(role, "user"))
|
|
role = "User";
|
|
else
|
|
role = "Security Officer";
|
|
|
|
if (!strcmp(usage, "pin")) {
|
|
snprintf(prompt, sizeof(prompt), "New %s PIN", role);
|
|
snprintf(label, sizeof(label), "%s PIN", role);
|
|
} else {
|
|
snprintf(prompt, sizeof(prompt),
|
|
"Unblock Code for New %s PIN", role);
|
|
snprintf(label, sizeof(label),
|
|
"%s unblocking PIN (PUK)", role);
|
|
}
|
|
|
|
hints->dialog_name = name;
|
|
hints->prompt = prompt;
|
|
hints->obj_label = label;
|
|
|
|
return get_pin(hints, retstr);
|
|
}
|
|
|
|
/*
|
|
* PIN retrieval callback
|
|
*/
|
|
static int
|
|
get_pin_callback(struct sc_profile *profile,
|
|
int id, const struct sc_pkcs15_auth_info *info,
|
|
const char *label,
|
|
u8 *pinbuf, size_t *pinsize)
|
|
{
|
|
char namebuf[64];
|
|
char *secret = NULL;
|
|
const char *name = NULL;
|
|
size_t len = 0;
|
|
int allocated = 0;
|
|
|
|
if (info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
if (label)
|
|
snprintf(namebuf, sizeof(namebuf), "PIN [%s]", label);
|
|
else
|
|
snprintf(namebuf, sizeof(namebuf), "Unspecified PIN [reference %u]", info->attrs.pin.reference);
|
|
|
|
if (!ignore_cmdline_pins) {
|
|
if (info->auth_method == SC_AC_SYMBOLIC) {
|
|
switch (id) {
|
|
case SC_PKCS15INIT_USER_PIN:
|
|
name = "User PIN";
|
|
secret = (char *) opt_pins[OPT_PIN1 & 3];
|
|
break;
|
|
case SC_PKCS15INIT_USER_PUK:
|
|
name = "User PIN unlock key";
|
|
secret = (char *) opt_pins[OPT_PUK1 & 3];
|
|
break;
|
|
case SC_PKCS15INIT_SO_PIN:
|
|
name = "Security officer PIN";
|
|
secret = (char *) opt_pins[OPT_PIN2 & 3];
|
|
break;
|
|
case SC_PKCS15INIT_SO_PUK:
|
|
name = "Security officer PIN unlock key";
|
|
secret = (char *) opt_pins[OPT_PUK2 & 3];
|
|
break;
|
|
}
|
|
}
|
|
else if (info->auth_method == SC_AC_CHV) {
|
|
if (!(info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
|
|
&& !(info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) {
|
|
name = "User PIN";
|
|
secret = (char *) opt_pins[OPT_PIN1 & 3];
|
|
}
|
|
else if (!(info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
|
|
&& (info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) {
|
|
name = "User PUK";
|
|
secret = (char *) opt_pins[OPT_PUK1 & 3];
|
|
}
|
|
else if ((info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
|
|
&& !(info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) {
|
|
name = "Security officer PIN";
|
|
secret = (char *) opt_pins[OPT_PIN2 & 3];
|
|
}
|
|
else if ((info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
|
|
&& (info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) {
|
|
name = "Security officer PIN unlock key";
|
|
secret = (char *) opt_pins[OPT_PUK2 & 3];
|
|
}
|
|
}
|
|
if (secret)
|
|
len = strlen(secret);
|
|
}
|
|
|
|
if (name && label)
|
|
snprintf(namebuf, sizeof(namebuf), "%s [%s]", name, label);
|
|
else if (name)
|
|
snprintf(namebuf, sizeof(namebuf), "%s", name);
|
|
|
|
name = namebuf;
|
|
|
|
/* See if we were given --secret @ID=.... */
|
|
if (!secret) {
|
|
unsigned int n;
|
|
|
|
for (n = 0; n < opt_secret_count; n++) {
|
|
struct secret *s = &opt_secrets[n];
|
|
|
|
if (sc_pkcs15_compare_id(&info->auth_id, &s->id)) {
|
|
secret = (char *) s->key;
|
|
len = s->len;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!secret) {
|
|
sc_ui_hints_t hints;
|
|
char prompt[128];
|
|
int r;
|
|
|
|
if (opt_use_pinpad)
|
|
return SC_ERROR_OBJECT_NOT_FOUND;
|
|
|
|
snprintf(prompt, sizeof(prompt), "%s required", name);
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.dialog_name = "pkcs15init.get_pin";
|
|
hints.prompt = prompt;
|
|
hints.obj_label = name;
|
|
hints.usage = SC_UI_USAGE_OTHER;
|
|
hints.card = card;
|
|
hints.p15card = p15card;
|
|
|
|
if ((r = get_pin(&hints, &secret)) < 0) {
|
|
fprintf(stderr,
|
|
"Failed to read PIN from user: %s\n",
|
|
sc_strerror(r));
|
|
return r;
|
|
}
|
|
|
|
len = strlen(secret);
|
|
allocated = 1;
|
|
}
|
|
|
|
if (len > *pinsize) {
|
|
if (allocated)
|
|
free(secret);
|
|
return SC_ERROR_BUFFER_TOO_SMALL;
|
|
}
|
|
memcpy(pinbuf, secret, len + 1);
|
|
*pinsize = len;
|
|
if (allocated)
|
|
free(secret);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
get_key_callback(struct sc_profile *profile,
|
|
int method, int reference,
|
|
const u8 *def_key, size_t def_key_size,
|
|
u8 *key_buf, size_t *buf_size)
|
|
{
|
|
const char *kind, *prompt, *key = NULL;
|
|
|
|
if (def_key_size && opt_use_defkeys) {
|
|
if (*buf_size < def_key_size)
|
|
return SC_ERROR_BUFFER_TOO_SMALL;
|
|
memcpy(key_buf, def_key, def_key_size);
|
|
*buf_size = def_key_size;
|
|
return 0;
|
|
}
|
|
|
|
switch (method) {
|
|
case SC_AC_PRO:
|
|
kind = "Secure messaging key";
|
|
break;
|
|
case SC_AC_AUT:
|
|
kind = "External authentication key";
|
|
break;
|
|
default: /* don't really know what sort of key */
|
|
kind = "Key";
|
|
break;
|
|
}
|
|
|
|
printf("Transport key (%s #%d) required.\n", kind, reference);
|
|
if (opt_use_pinpad) {
|
|
printf("\n"
|
|
"Refusing to prompt for transport key because --use-pinpad\n"
|
|
"was specified on the command line. Please invoke without\n"
|
|
"--no-prompt, or specify the --use-default-transport-keys\n"
|
|
"option to use the default transport keys without being\n"
|
|
"prompted.\n");
|
|
fprintf(stderr, "Aborting.\n");
|
|
exit(1);
|
|
}
|
|
|
|
printf("Please enter key in hexadecimal notation "
|
|
"(e.g. 00:11:22:aa:bb:cc)%s.\n\n",
|
|
def_key_size? ",\nor press return to accept default" : "");
|
|
printf("To use the default transport keys without being prompted,\n"
|
|
"specify the --use-default-transport-keys option on the\n"
|
|
"command line (or -T for short), or press Ctrl-C to abort.\n");
|
|
|
|
while (1) {
|
|
char buffer[256];
|
|
|
|
prompt = "Please enter key";
|
|
if (def_key_size && def_key_size < 64) {
|
|
unsigned int j, k = 0;
|
|
|
|
sprintf(buffer, "%s [", prompt);
|
|
k = strlen(buffer);
|
|
for (j = 0; j < def_key_size; j++, k += 2) {
|
|
if (j) buffer[k++] = ':';
|
|
sprintf(buffer+k, "%02x", def_key[j]);
|
|
}
|
|
buffer[k++] = ']';
|
|
buffer[k++] = '\0';
|
|
prompt = buffer;
|
|
}
|
|
|
|
printf("%s: ", prompt);
|
|
fflush(stdout);
|
|
#ifdef GET_KEY_ECHO_OFF
|
|
do {
|
|
size_t len = 0;
|
|
int r;
|
|
|
|
/* Read key with echo off - will users really manage? */
|
|
r = util_getpass(&key, &len, stdin);
|
|
if (r < 0 || !key)
|
|
return SC_ERROR_INTERNAL;
|
|
} while(0);
|
|
#else
|
|
key = fgets(buffer, sizeof(buffer), stdin);
|
|
if (key)
|
|
buffer[strcspn(buffer, "\r\n")] = '\0';
|
|
#endif
|
|
if (key == NULL)
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
if (key[0] == '\0' && def_key_size) {
|
|
if (*buf_size < def_key_size)
|
|
return SC_ERROR_BUFFER_TOO_SMALL;
|
|
memcpy(key_buf, def_key, def_key_size);
|
|
*buf_size = def_key_size;
|
|
return 0;
|
|
}
|
|
|
|
if (sc_hex_to_bin(key, key_buf, buf_size) >= 0)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Read a private key
|
|
*/
|
|
static int pass_cb(char *buf, int len, int flags, void *d)
|
|
{
|
|
size_t pass_len = 0;
|
|
int plen, r;
|
|
char *pass = (char *)d;
|
|
|
|
if (!pass) {
|
|
printf("Please enter passphrase to unlock secret key: ");
|
|
r = util_getpass(&pass, &pass_len, stdin);
|
|
if (r < 0 || !pass)
|
|
return 0;
|
|
}
|
|
|
|
plen = strlen(pass);
|
|
if (plen <= 0)
|
|
return 0;
|
|
if (plen > len)
|
|
plen = len;
|
|
memcpy(buf, pass, plen);
|
|
return plen;
|
|
}
|
|
|
|
static int
|
|
do_read_pem_private_key(const char *filename, const char *passphrase,
|
|
EVP_PKEY **key)
|
|
{
|
|
BIO *bio;
|
|
|
|
bio = BIO_new(BIO_s_file());
|
|
if (BIO_read_filename(bio, filename) <= 0)
|
|
util_fatal("Unable to open %s: %m", filename);
|
|
*key = PEM_read_bio_PrivateKey(bio, NULL, pass_cb, (char *) passphrase);
|
|
BIO_free(bio);
|
|
if (*key == NULL) {
|
|
ossl_print_errors();
|
|
return SC_ERROR_CANNOT_LOAD_KEY;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
do_read_pkcs12_private_key(const char *filename, const char *passphrase,
|
|
EVP_PKEY **key, X509 **certs, unsigned int max_certs)
|
|
{
|
|
BIO *bio;
|
|
PKCS12 *p12;
|
|
EVP_PKEY *user_key = NULL;
|
|
X509 *user_cert = NULL;
|
|
STACK_OF(X509) *cacerts = NULL;
|
|
int i, ncerts = 0;
|
|
|
|
*key = NULL;
|
|
|
|
bio = BIO_new(BIO_s_file());
|
|
if (BIO_read_filename(bio, filename) <= 0)
|
|
util_fatal("Unable to open %s: %m", filename);
|
|
p12 = d2i_PKCS12_bio(bio, NULL);
|
|
BIO_free(bio);
|
|
|
|
if (p12 == NULL
|
|
|| !PKCS12_parse(p12, passphrase, &user_key, &user_cert, &cacerts))
|
|
goto error;
|
|
|
|
if (!user_key) {
|
|
util_error("No key in pkcs12 file?!\n");
|
|
return SC_ERROR_CANNOT_LOAD_KEY;
|
|
}
|
|
|
|
EVP_PKEY_up_ref(user_key);
|
|
|
|
if (user_cert && max_certs)
|
|
certs[ncerts++] = user_cert;
|
|
|
|
/* Extract CA certificates, if any */
|
|
for(i = 0; cacerts && ncerts < (int)max_certs && i < sk_X509_num(cacerts); i++)
|
|
certs[ncerts++] = sk_X509_value(cacerts, i);
|
|
|
|
/* bump reference counts for certificates */
|
|
for (i = 0; i < ncerts; i++) {
|
|
X509_up_ref(certs[i]);
|
|
}
|
|
|
|
if (cacerts)
|
|
sk_X509_free(cacerts);
|
|
|
|
*key = user_key;
|
|
return ncerts;
|
|
|
|
error: ossl_print_errors();
|
|
return SC_ERROR_CANNOT_LOAD_KEY;
|
|
}
|
|
|
|
static int
|
|
do_read_private_key(const char *filename, const char *format,
|
|
EVP_PKEY **pk, X509 **certs, unsigned int max_certs)
|
|
{
|
|
size_t len = 0;
|
|
char *passphrase = NULL;
|
|
int r;
|
|
|
|
if (opt_passphrase)
|
|
passphrase = (char *) opt_passphrase;
|
|
|
|
if (!format || !strcasecmp(format, "pem")) {
|
|
r = do_read_pem_private_key(filename, passphrase, pk);
|
|
} else if (!strcasecmp(format, "pkcs12")) {
|
|
r = do_read_pkcs12_private_key(filename,
|
|
passphrase, pk, certs, max_certs);
|
|
if (r < 0 && !passphrase) {
|
|
/* this makes only sense for PKCS#12
|
|
* PKCS12_parse must support passphrases with
|
|
* length zero and NULL because of the specification
|
|
* of PKCS12 - please see the sourcecode of OpenSSL
|
|
* therefore OpenSSL does not ask for a passphrase like
|
|
* the PEM interface
|
|
* see OpenSSL: crypto/pkcs12/p12_kiss.c
|
|
*/
|
|
printf("Please enter passphrase to unlock secret key: ");
|
|
r = util_getpass(&passphrase, &len, stdin);
|
|
if (r < 0 || !passphrase)
|
|
return SC_ERROR_INTERNAL;
|
|
r = do_read_pkcs12_private_key(filename,
|
|
passphrase, pk, certs, max_certs);
|
|
}
|
|
} else {
|
|
util_error("Error when reading private key. "
|
|
"Key file format \"%s\" not supported.\n", format);
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (NULL == opt_passphrase)
|
|
free(passphrase);
|
|
|
|
if (r < 0)
|
|
util_fatal("Unable to read private key from %s\n", filename);
|
|
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Read a public key
|
|
*/
|
|
static EVP_PKEY *
|
|
do_read_pem_public_key(const char *filename)
|
|
{
|
|
BIO *bio;
|
|
EVP_PKEY *pk;
|
|
|
|
bio = BIO_new(BIO_s_file());
|
|
if (BIO_read_filename(bio, filename) <= 0)
|
|
util_fatal("Unable to open %s: %m", filename);
|
|
pk = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
|
|
BIO_free(bio);
|
|
if (pk == NULL)
|
|
ossl_print_errors();
|
|
return pk;
|
|
}
|
|
|
|
static EVP_PKEY *
|
|
do_read_der_public_key(const char *filename)
|
|
{
|
|
BIO *bio;
|
|
EVP_PKEY *pk;
|
|
|
|
bio = BIO_new(BIO_s_file());
|
|
if (BIO_read_filename(bio, filename) <= 0)
|
|
util_fatal("Unable to open %s: %m", filename);
|
|
pk = d2i_PUBKEY_bio(bio, NULL);
|
|
BIO_free(bio);
|
|
if (pk == NULL)
|
|
ossl_print_errors();
|
|
return pk;
|
|
}
|
|
|
|
static int
|
|
do_read_public_key(const char *name, const char *format, EVP_PKEY **out)
|
|
{
|
|
if (!format || !strcasecmp(format, "pem")) {
|
|
*out = do_read_pem_public_key(name);
|
|
} else if (!strcasecmp(format, "der")) {
|
|
*out = do_read_der_public_key(name);
|
|
} else {
|
|
util_fatal("Error when reading public key. "
|
|
"File format \"%s\" not supported.\n",
|
|
format);
|
|
}
|
|
|
|
if (!*out)
|
|
util_fatal("Unable to read public key from %s\n", name);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read a certificate
|
|
*/
|
|
static X509 *
|
|
do_read_pem_certificate(const char *filename)
|
|
{
|
|
BIO *bio;
|
|
X509 *xp;
|
|
|
|
bio = BIO_new(BIO_s_file());
|
|
if (BIO_read_filename(bio, filename) <= 0)
|
|
util_fatal("Unable to open %s: %m", filename);
|
|
xp = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
|
BIO_free(bio);
|
|
if (xp == NULL)
|
|
ossl_print_errors();
|
|
return xp;
|
|
}
|
|
|
|
static X509 *
|
|
do_read_der_certificate(const char *filename)
|
|
{
|
|
BIO *bio;
|
|
X509 *xp;
|
|
|
|
bio = BIO_new(BIO_s_file());
|
|
if (BIO_read_filename(bio, filename) <= 0)
|
|
util_fatal("Unable to open %s: %m", filename);
|
|
xp = d2i_X509_bio(bio, NULL);
|
|
BIO_free(bio);
|
|
if (xp == NULL)
|
|
ossl_print_errors();
|
|
return xp;
|
|
}
|
|
|
|
static int
|
|
do_read_certificate(const char *name, const char *format, X509 **out)
|
|
{
|
|
if (!format || !strcasecmp(format, "pem")) {
|
|
*out = do_read_pem_certificate(name);
|
|
} else if (!strcasecmp(format, "der")) {
|
|
*out = do_read_der_certificate(name);
|
|
} else {
|
|
util_fatal("Error when reading certificate. "
|
|
"File format \"%s\" not supported.\n",
|
|
format);
|
|
}
|
|
|
|
if (!*out)
|
|
util_fatal("Unable to read certificate from %s\n", name);
|
|
return 0;
|
|
}
|
|
|
|
static size_t determine_filesize(const char *filename)
|
|
{
|
|
FILE *fp;
|
|
long ll;
|
|
|
|
if ((fp = fopen(filename,"rb")) == NULL)
|
|
util_fatal("Unable to open %s: %m", filename);
|
|
|
|
fseek(fp,0L,SEEK_END);
|
|
ll = ftell(fp);
|
|
if (ll == -1l)
|
|
util_fatal("fseek/ftell error");
|
|
|
|
fclose(fp);
|
|
return (size_t)ll;
|
|
}
|
|
|
|
static int
|
|
do_read_data_object(const char *name, u8 **out, size_t *outlen, size_t expected)
|
|
{
|
|
FILE *inf;
|
|
size_t filesize = expected ? expected : determine_filesize(name);
|
|
int c;
|
|
|
|
*out = malloc(filesize);
|
|
if (*out == NULL)
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
|
inf = fopen(name, "rb");
|
|
if (inf == NULL) {
|
|
fprintf(stderr, "Unable to open '%s' for reading.\n", name);
|
|
return -1;
|
|
}
|
|
c = fread(*out, 1, filesize, inf);
|
|
fclose(inf);
|
|
if (c < 0) {
|
|
perror("read");
|
|
return -1;
|
|
}
|
|
|
|
*outlen = filesize;
|
|
return 0;
|
|
}
|
|
|
|
static char *
|
|
cert_common_name(X509 *x509)
|
|
{
|
|
X509_NAME_ENTRY *ne = NULL;
|
|
ASN1_STRING *a_str = NULL;
|
|
char *out = NULL;
|
|
unsigned char *tmp = NULL;
|
|
int idx, out_len = 0;
|
|
|
|
idx = X509_NAME_get_index_by_NID(X509_get_subject_name(x509), NID_commonName, -1);
|
|
if (idx < 0)
|
|
return NULL;
|
|
|
|
ne = X509_NAME_get_entry(X509_get_subject_name(x509), idx);
|
|
if (!ne)
|
|
return NULL;
|
|
|
|
a_str = X509_NAME_ENTRY_get_data(ne);
|
|
if (!a_str)
|
|
return NULL;
|
|
|
|
out_len = ASN1_STRING_to_UTF8(&tmp, a_str);
|
|
if (!tmp)
|
|
return NULL;
|
|
|
|
out = calloc(1, out_len + 1);
|
|
if (out)
|
|
memcpy(out, tmp, out_len);
|
|
OPENSSL_free(tmp);
|
|
|
|
return out;
|
|
}
|
|
|
|
|
|
static int do_convert_cert(sc_pkcs15_der_t *der, X509 *cert)
|
|
{
|
|
u8 *p;
|
|
|
|
der->len = i2d_X509(cert, NULL);
|
|
der->value = p = malloc(der->len);
|
|
i2d_X509(cert, &p);
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int
|
|
parse_objects(const char *list, unsigned int action)
|
|
{
|
|
unsigned int res = 0;
|
|
static struct {
|
|
const char *name;
|
|
unsigned int flag;
|
|
} del_flags[] = {
|
|
{"privkey", SC_PKCS15INIT_TYPE_PRKEY},
|
|
{"pubkey", SC_PKCS15INIT_TYPE_PUBKEY},
|
|
{"cert", SC_PKCS15INIT_TYPE_CERT},
|
|
{"chain", SC_PKCS15INIT_TYPE_CHAIN},
|
|
{"data", SC_PKCS15INIT_TYPE_DATA},
|
|
{"secrkey", SC_PKCS15INIT_TYPE_SKEY},
|
|
{NULL, 0}
|
|
};
|
|
|
|
while (1) {
|
|
int len, n;
|
|
|
|
while (*list == ',')
|
|
list++;
|
|
if (!*list)
|
|
break;
|
|
len = strcspn(list, ",");
|
|
if (len == 4 && !strncasecmp(list, "help", 4)) {
|
|
if (action == ACTION_DELETE_OBJECTS) {
|
|
printf("\nDelete arguments: a comma-separated list containing any of the following:\n");
|
|
printf(" privkey,pubkey,secrkey,cert,chain,data\n");
|
|
printf("When \"data\" is specified, an --application-id must also be specified,\n");
|
|
printf(" in the other cases an \"--id\" must also be specified\n");
|
|
printf("When \"chain\" is specified, the certificate chain starting with the cert\n");
|
|
printf(" with specified ID will be deleted, untill there's a CA cert that certifies\n");
|
|
printf(" another cert on the card\n");
|
|
}
|
|
else {
|
|
printf("\nChange attribute argument: either privkey, pubkey, secrkey, cert or data\n");
|
|
printf("You also have to specify the --id of the object\n");
|
|
printf("For now, you can only change the --label\n");
|
|
printf("E.g. pkcs15-init -A cert --id 45 -a 1 --label Jim\n");
|
|
}
|
|
exit(0);
|
|
}
|
|
for (n = 0; del_flags[n].name; n++) {
|
|
if (!strncasecmp(del_flags[n].name, list, len)) {
|
|
res |= del_flags[n].flag;
|
|
break;
|
|
}
|
|
}
|
|
if (del_flags[n].name == NULL) {
|
|
fprintf(stderr, "Unknown argument for --delete_objects: %.*s\n", len, list);
|
|
exit(0);
|
|
}
|
|
list += len;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Parse X.509 key usage list
|
|
*/
|
|
static void
|
|
parse_x509_usage(const char *list, unsigned int *res)
|
|
{
|
|
static struct {
|
|
const char* name;
|
|
unsigned int flag;
|
|
} x509_usage_names[] = {
|
|
{ "digitalSignature", SC_PKCS15INIT_X509_DIGITAL_SIGNATURE },
|
|
{ "nonRepudiation", SC_PKCS15INIT_X509_NON_REPUDIATION },
|
|
{ "keyEncipherment", SC_PKCS15INIT_X509_KEY_ENCIPHERMENT },
|
|
{ "dataEncipherment", SC_PKCS15INIT_X509_DATA_ENCIPHERMENT },
|
|
{ "keyAgreement", SC_PKCS15INIT_X509_KEY_AGREEMENT },
|
|
{ "keyCertSign", SC_PKCS15INIT_X509_KEY_CERT_SIGN },
|
|
{ "cRLSign", SC_PKCS15INIT_X509_CRL_SIGN },
|
|
{ NULL, 0 }
|
|
};
|
|
static struct {
|
|
const char * name;
|
|
const char * list;
|
|
} x509_usage_aliases[] = {
|
|
{ "sign", "digitalSignature,keyCertSign,cRLSign" },
|
|
{ "decrypt", "keyEncipherment,dataEncipherment" },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
while (1) {
|
|
int len, n, match = 0;
|
|
|
|
while (*list == ',')
|
|
list++;
|
|
if (!*list)
|
|
break;
|
|
len = strcspn(list, ",");
|
|
if (len == 4 && !strncasecmp(list, "help", 4)) {
|
|
printf("Valid X.509 usage names (case-insensitive):\n");
|
|
for (n = 0; x509_usage_names[n].name; n++)
|
|
printf(" %s\n", x509_usage_names[n].name);
|
|
printf("\nAliases:\n");
|
|
for (n = 0; x509_usage_aliases[n].name; n++) {
|
|
printf(" %-12s %s\n",
|
|
x509_usage_aliases[n].name,
|
|
x509_usage_aliases[n].list);
|
|
}
|
|
printf("\nUse commas to separate several usage names.\n"
|
|
"Abbreviated names are okay if unique (e.g. dataEnc)\n");
|
|
exit(0);
|
|
}
|
|
for (n = 0; x509_usage_names[n].name != NULL; n++) {
|
|
if (!strncasecmp(x509_usage_names[n].name, list, len)) {
|
|
*res |= x509_usage_names[n].flag;
|
|
match++;
|
|
}
|
|
}
|
|
for (n = 0; x509_usage_aliases[n].name; n++) {
|
|
if (!strncasecmp(x509_usage_aliases[n].name, list, len)) {
|
|
parse_x509_usage(x509_usage_aliases[n].list, res);
|
|
match++;
|
|
}
|
|
}
|
|
if (match == 0) {
|
|
fprintf(stderr,
|
|
"Unknown X.509 key usage %.*s\n", len, list);
|
|
exit(1);
|
|
}
|
|
if (match > 1) {
|
|
fprintf(stderr,
|
|
"Ambiguous X.509 key usage %.*s\n", len, list);
|
|
exit(1);
|
|
}
|
|
list += len;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle one option
|
|
*/
|
|
static void
|
|
handle_option(const struct option *opt)
|
|
{
|
|
unsigned int this_action = ACTION_NONE;
|
|
|
|
switch (opt->val) {
|
|
case 'a':
|
|
opt_authid = optarg;
|
|
break;
|
|
case 'C':
|
|
this_action = ACTION_INIT;
|
|
break;
|
|
case 'E':
|
|
this_action = ACTION_ERASE;
|
|
break;
|
|
case 'G':
|
|
this_action = ACTION_GENERATE_KEY;
|
|
opt_newkey = optarg;
|
|
break;
|
|
case 'S':
|
|
this_action = ACTION_STORE_PRIVKEY;
|
|
opt_infile = optarg;
|
|
break;
|
|
case 'P':
|
|
this_action = ACTION_STORE_PIN;
|
|
break;
|
|
case 'X':
|
|
this_action = ACTION_STORE_CERT;
|
|
opt_infile = optarg;
|
|
break;
|
|
case 'U':
|
|
this_action = ACTION_UPDATE_CERT;
|
|
opt_infile = optarg;
|
|
break;
|
|
case 'W':
|
|
this_action = ACTION_STORE_DATA;
|
|
opt_infile = optarg;
|
|
break;
|
|
case 'D':
|
|
this_action = ACTION_DELETE_OBJECTS;
|
|
opt_delete_flags = parse_objects(optarg, ACTION_DELETE_OBJECTS);
|
|
break;
|
|
case 'A':
|
|
this_action = ACTION_CHANGE_ATTRIBUTES;
|
|
opt_type = parse_objects(optarg, ACTION_CHANGE_ATTRIBUTES);
|
|
break;
|
|
case 'v':
|
|
verbose++;
|
|
break;
|
|
case 'f':
|
|
opt_format = optarg;
|
|
break;
|
|
case 'h':
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
|
/* exit */
|
|
case 'i':
|
|
opt_objectid = optarg;
|
|
break;
|
|
case 'l':
|
|
opt_label = optarg;
|
|
break;
|
|
case 'o':
|
|
opt_outkey = optarg;
|
|
break;
|
|
case 'p':
|
|
opt_profile = optarg;
|
|
break;
|
|
case 'c':
|
|
opt_card_profile = optarg;
|
|
break;
|
|
case 'r':
|
|
opt_reader = optarg;
|
|
break;
|
|
case 'u':
|
|
parse_x509_usage(optarg, &opt_x509_usage);
|
|
break;
|
|
case 'w':
|
|
opt_wait = 1;
|
|
break;
|
|
case OPT_OPTIONS:
|
|
read_options_file(optarg);
|
|
break;
|
|
case OPT_PIN1: case OPT_PUK1:
|
|
case OPT_PIN2: case OPT_PUK2:
|
|
util_get_pin(optarg, &(opt_pins[opt->val & 3]));
|
|
break;
|
|
case OPT_SERIAL:
|
|
opt_serial = optarg;
|
|
break;
|
|
case OPT_PASSPHRASE:
|
|
util_get_pin(optarg, &opt_passphrase);
|
|
break;
|
|
case OPT_PUBKEY:
|
|
this_action = ACTION_STORE_PUBKEY;
|
|
opt_infile = optarg;
|
|
break;
|
|
case OPT_SECRKEY:
|
|
this_action = ACTION_STORE_SECRKEY;
|
|
opt_infile = optarg;
|
|
break;
|
|
case OPT_INSECURE:
|
|
opt_insecure = 1;
|
|
break;
|
|
case OPT_EXTRACTABLE:
|
|
opt_extractable = 1;
|
|
break;
|
|
case OPT_AUTHORITY:
|
|
opt_authority = 1;
|
|
break;
|
|
case OPT_APPLICATION_NAME:
|
|
opt_application_name = optarg;
|
|
break;
|
|
case OPT_APPLICATION_ID:
|
|
opt_application_id = optarg;
|
|
break;
|
|
case OPT_BIND_TO_AID:
|
|
opt_bind_to_aid = optarg;
|
|
break;
|
|
case OPT_PUK_ID:
|
|
opt_puk_authid = optarg;
|
|
break;
|
|
case OPT_PUK_LABEL:
|
|
opt_puk_label = optarg;
|
|
break;
|
|
case 'T':
|
|
opt_use_defkeys = 1;
|
|
break;
|
|
case OPT_NO_SOPIN:
|
|
opt_no_sopin = 1;
|
|
break;
|
|
case OPT_USE_PINPAD_DEPRECATED:
|
|
fprintf(stderr, "'--no-prompt' is deprecated , use '--use-pinpad' instead.\n");
|
|
/* fall through */
|
|
case OPT_USE_PINPAD:
|
|
opt_use_pinpad = 1;
|
|
break;
|
|
case OPT_ASSERT_PRISTINE:
|
|
this_action = ACTION_ASSERT_PRISTINE;
|
|
break;
|
|
case OPT_SECRET:
|
|
parse_secret(&opt_secrets[opt_secret_count], optarg);
|
|
opt_secret_count++;
|
|
break;
|
|
case OPT_SECRKEY_ALGO:
|
|
opt_secrkey_algo = optarg;
|
|
break;
|
|
case OPT_PUBKEY_LABEL:
|
|
opt_pubkey_label = optarg;
|
|
break;
|
|
case 'F':
|
|
this_action = ACTION_FINALIZE_CARD;
|
|
break;
|
|
case OPT_CERT_LABEL:
|
|
opt_cert_label = optarg;
|
|
break;
|
|
case OPT_VERIFY_PIN:
|
|
opt_verify_pin = 1;
|
|
break;
|
|
case OPT_SANITY_CHECK:
|
|
this_action = ACTION_SANITY_CHECK;
|
|
break;
|
|
case OPT_UPDATE_LAST_UPDATE:
|
|
this_action = ACTION_UPDATE_LAST_UPDATE;
|
|
break;
|
|
case OPT_ERASE_APPLICATION:
|
|
opt_bind_to_aid = optarg;
|
|
this_action = ACTION_ERASE_APPLICATION;
|
|
break;
|
|
case OPT_IGNORE_CA_CERTIFICATES:
|
|
opt_ignore_ca_certs = 1;
|
|
break;
|
|
case OPT_UPDATE_EXISTING:
|
|
opt_update_existing = 1;
|
|
break;
|
|
case OPT_MD_CONTAINER_GUID:
|
|
opt_md_container_guid = optarg;
|
|
break;
|
|
case OPT_VERSION:
|
|
this_action = ACTION_PRINT_VERSION;
|
|
break;
|
|
default:
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
|
}
|
|
|
|
if ((opt_actions & (1 << this_action)) && opt->has_arg != no_argument) {
|
|
fprintf(stderr, "Error: you cannot specify option");
|
|
if (opt->name)
|
|
fprintf(stderr, " --%s", opt->name);
|
|
if (isprint(opt->val))
|
|
fprintf(stderr, " -%c", opt->val);
|
|
fprintf(stderr, " more than once.\n");
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
|
}
|
|
if (this_action)
|
|
opt_actions |= (1 << this_action);
|
|
|
|
if ((opt_pins[OPT_PIN2&3] || opt_pins[OPT_PUK2&3]) && opt_no_sopin) {
|
|
fprintf(stderr, "Error: "
|
|
"The --no-so-pin option and --so-pin/--so-puk are mutually"
|
|
"exclusive.\n");
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
|
}
|
|
|
|
if ((opt_actions & (1 << ACTION_ERASE)) &&
|
|
(opt_actions != (1 << ACTION_ERASE))) {
|
|
fprintf(stderr, "Error: erasing a card is incompatible with all other actions\n");
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse the command line.
|
|
*/
|
|
static void
|
|
parse_commandline(int argc, char **argv)
|
|
{
|
|
const struct option *o;
|
|
char shortopts[64], *sp;
|
|
int c, i;
|
|
|
|
/* We make sure the list of short options is always
|
|
* consistent with the long options */
|
|
for (o = options, sp = shortopts; o->name; o++) {
|
|
if (o->val <= 0 || o->val >= 256)
|
|
continue;
|
|
*sp++ = o->val;
|
|
switch (o->has_arg) {
|
|
case optional_argument:
|
|
*sp++ = ':';
|
|
/* fall through */
|
|
case required_argument:
|
|
*sp++ = ':';
|
|
case no_argument:
|
|
break;
|
|
default:
|
|
util_fatal("Internal: bad has_arg value");
|
|
}
|
|
}
|
|
sp[0] = 0;
|
|
|
|
while ((c = getopt_long(argc, argv, shortopts, options, &i)) != -1) {
|
|
/* The optindex seems to be off with some glibc
|
|
* getopt implementations */
|
|
for (o = options; o->name; o++) {
|
|
if (o->val == c) {
|
|
handle_option(o);
|
|
goto next;
|
|
}
|
|
}
|
|
util_fatal("Internal error in options handling, option %u\n", c);
|
|
|
|
next: ;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Read a file containing more command line options.
|
|
* This allows you to specify PINs to pkcs15-init without
|
|
* exposing them through ps.
|
|
*/
|
|
static void
|
|
read_options_file(const char *filename)
|
|
{
|
|
const struct option *o;
|
|
char buffer[1024], *name;
|
|
FILE *fp;
|
|
|
|
if ((fp = fopen(filename, "r")) == NULL)
|
|
util_fatal("Unable to open %s: %m", filename);
|
|
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
|
|
buffer[strcspn(buffer, "\n")] = '\0';
|
|
|
|
name = strtok(buffer, " \t");
|
|
while (name) {
|
|
if (*name == '#')
|
|
break;
|
|
for (o = options; o->name; o++)
|
|
if (!strcmp(o->name, name))
|
|
break;
|
|
if (!o->name) {
|
|
util_error("Unknown option \"%s\"\n", name);
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
|
}
|
|
if (o->has_arg != no_argument) {
|
|
optarg = strtok(NULL, "");
|
|
if (optarg) {
|
|
while (isspace((int) *optarg))
|
|
optarg++;
|
|
optarg = strdup(optarg);
|
|
}
|
|
}
|
|
if (o->has_arg == required_argument
|
|
&& (!optarg || !*optarg)) {
|
|
util_error("Option %s: missing argument\n", name);
|
|
util_print_usage_and_die(app_name, options, option_help, NULL);
|
|
}
|
|
handle_option(o);
|
|
name = strtok(NULL, " \t");
|
|
}
|
|
}
|
|
fclose(fp);
|
|
}
|
|
|
|
|
|
/*
|
|
* OpenSSL helpers
|
|
*/
|
|
static void
|
|
ossl_print_errors(void)
|
|
{
|
|
static int loaded = 0;
|
|
long err;
|
|
|
|
if (!loaded) {
|
|
ERR_load_crypto_strings();
|
|
loaded = 1;
|
|
}
|
|
|
|
while ((err = ERR_get_error()) != 0)
|
|
fprintf(stderr, "%s\n", ERR_error_string(err, NULL));
|
|
}
|
|
|
|
/*
|
|
* Retrieve a PIN from the user.
|
|
*
|
|
* @hints dialog hints
|
|
* @out PIN entered by the user; must be freed.
|
|
* NULL if dialog was canceled.
|
|
*/
|
|
int get_pin(sc_ui_hints_t *hints, char **out)
|
|
{
|
|
sc_pkcs15_auth_info_t *pin_info;
|
|
const char *label;
|
|
int flags = hints->flags;
|
|
|
|
pin_info = hints->info.pin;
|
|
if (pin_info && (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN))
|
|
return SC_ERROR_NOT_SUPPORTED;
|
|
|
|
if (!(label = hints->obj_label)) {
|
|
if (pin_info == NULL)
|
|
label = "PIN";
|
|
else if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
|
|
label = "Security Officer PIN";
|
|
else
|
|
label = "User PIN";
|
|
}
|
|
|
|
if (hints->p15card) {
|
|
/* TBD: get preferredCard from TokenInfo */
|
|
}
|
|
|
|
if (hints->prompt) {
|
|
printf("%s", hints->prompt);
|
|
if (flags & SC_UI_PIN_OPTIONAL)
|
|
printf(" (Optional - press return for no PIN)");
|
|
printf(".\n");
|
|
}
|
|
|
|
*out = NULL;
|
|
while (1) {
|
|
char *pin = NULL;
|
|
size_t len = 0;
|
|
int r;
|
|
|
|
printf("Please enter %s: ", label);
|
|
r = util_getpass(&pin, &len, stdin);
|
|
if (r < 0 || !pin)
|
|
return SC_ERROR_INTERNAL;
|
|
|
|
if (!strlen(pin) && (flags & SC_UI_PIN_OPTIONAL))
|
|
return 0;
|
|
|
|
if (pin_info && (flags & SC_UI_PIN_CHECK_LENGTH)) {
|
|
if (strlen(pin) < pin_info->attrs.pin.min_length) {
|
|
fprintf(stderr,
|
|
"PIN too short (min %lu characters)\n",
|
|
(unsigned long) pin_info->attrs.pin.min_length);
|
|
continue;
|
|
}
|
|
if (pin_info->attrs.pin.max_length
|
|
&& strlen(pin) > pin_info->attrs.pin.max_length) {
|
|
fprintf(stderr,
|
|
"PIN too long (max %lu characters)\n",
|
|
(unsigned long) pin_info->attrs.pin.max_length);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
*out = strdup(pin);
|
|
sc_mem_clear(pin, len);
|
|
|
|
if (!(flags & SC_UI_PIN_RETYPE))
|
|
break;
|
|
|
|
printf("Please type again to verify: ");
|
|
r = util_getpass(&pin, &len, stdin);
|
|
if (r < 0 || !pin)
|
|
return SC_ERROR_INTERNAL;
|
|
if (!strcmp(*out, pin)) {
|
|
sc_mem_clear(pin, len);
|
|
break;
|
|
}
|
|
|
|
free(*out);
|
|
*out = NULL;
|
|
|
|
if (!(flags & SC_UI_PIN_MISMATCH_RETRY)) {
|
|
fprintf(stderr, "PINs do not match.\n");
|
|
free(pin);
|
|
return SC_ERROR_KEYPAD_PIN_MISMATCH;
|
|
}
|
|
|
|
fprintf(stderr,
|
|
"Sorry, the two pins did not match. "
|
|
"Please try again.\n");
|
|
sc_mem_clear(pin, strlen(pin));
|
|
|
|
/* Currently, there's no way out of this dialog.
|
|
* We should allow the user to bail out after n
|
|
* attempts. */
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int verify_pin(struct sc_pkcs15_card *p15card, char *auth_id_str)
|
|
{
|
|
struct sc_pkcs15_object *pin_obj = NULL;
|
|
char pin_label[(sizeof pin_obj->label) + 20];
|
|
char *pin = NULL;
|
|
int r;
|
|
|
|
if (!auth_id_str) {
|
|
struct sc_pkcs15_object *objs[32];
|
|
int ii;
|
|
|
|
r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32);
|
|
if (r < 0) {
|
|
fprintf(stderr, "PIN code enumeration failed: %s\n", sc_strerror(r));
|
|
return -1;
|
|
}
|
|
|
|
for (ii=0;ii<r;ii++) {
|
|
struct sc_pkcs15_auth_info *pin_info = (struct sc_pkcs15_auth_info *) objs[ii]->data;
|
|
|
|
if (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
|
|
continue;
|
|
if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
|
|
continue;
|
|
if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)
|
|
continue;
|
|
|
|
pin_obj = objs[ii];
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
struct sc_pkcs15_id auth_id;
|
|
|
|
sc_pkcs15_hex_string_to_id(auth_id_str, &auth_id);
|
|
r = sc_pkcs15_find_pin_by_auth_id(p15card, &auth_id, &pin_obj);
|
|
if (r) {
|
|
fprintf(stderr, "Unable to find PIN code: %s\n", sc_strerror(r));
|
|
return r;
|
|
}
|
|
}
|
|
|
|
if (!pin_obj) {
|
|
fprintf(stderr, "PIN object '%s' not found\n", auth_id_str);
|
|
return -1;
|
|
}
|
|
|
|
if (opt_pins[0] != NULL) {
|
|
pin = (char *) opt_pins[0];
|
|
}
|
|
else {
|
|
sc_ui_hints_t hints;
|
|
|
|
if (opt_use_pinpad)
|
|
return SC_ERROR_OBJECT_NOT_FOUND;
|
|
|
|
if (pin_obj->label[0])
|
|
snprintf(pin_label, sizeof(pin_label), "User PIN [%.*s]",
|
|
(int) sizeof pin_obj->label, pin_obj->label);
|
|
else
|
|
snprintf(pin_label, sizeof(pin_label), "User PIN");
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.dialog_name = "pkcs15init.get_pin";
|
|
hints.prompt = "User PIN required";
|
|
hints.obj_label = pin_label;
|
|
hints.usage = SC_UI_USAGE_OTHER;
|
|
hints.card = card;
|
|
hints.p15card = p15card;
|
|
|
|
get_pin(&hints, &pin);
|
|
}
|
|
|
|
r = sc_pkcs15_verify_pin(p15card, pin_obj, (unsigned char *)pin, pin ? strlen((char *) pin) : 0);
|
|
if (r < 0)
|
|
fprintf(stderr, "Operation failed: %s\n", sc_strerror(r));
|
|
|
|
if (NULL == opt_pins[0])
|
|
free(pin);
|
|
|
|
return r;
|
|
}
|