diff --git a/doc/tools/sc-hsm-tool.1.xml b/doc/tools/sc-hsm-tool.1.xml
new file mode 100644
index 00000000..4c42439a
--- /dev/null
+++ b/doc/tools/sc-hsm-tool.1.xml
@@ -0,0 +1,217 @@
+
+
+
+ sc-hsm-tool
+ 1
+ OpenSC
+ OpenSC Tools
+ opensc
+
+
+
+ sc-hsm-tool
+ smart card utility for SmartCard-HSM
+
+
+
+
+ sc-hsm-tool
+ OPTIONS
+
+
+
+
+
+ The sc-hsm-tool utility can be used from the command line to perform
+ extended maintenance tasks not available via PKCS#11 or other tools in the OpenSC package.
+ It can be used to query the status of a SmartCard-HSM, initialize a device, generate and import
+ Device Key Encryption Key (DKEK) shares and to wrap and unwrap keys.
+
+
+
+
+ Options
+
+
+
+
+ ,
+
+
+
+ Initialize token, removing all existing keys, certificates and files.
+ Use to define SO-PIN for first initialization or to verify in subsequent
+ initializations.
+ Use to define the initial user pin value.
+ Use to define the maximum number of wrong user PIN presentations.
+ Use with to enable key wrap / unwrap.
+
+
+
+
+
+ filename,
+ filename
+
+
+ Create a DKEK share encrypted under a user supplied password and saved to the file
+ given as parameter.
+ Use to provide a password for encryption rather than prompting for one.
+
+
+
+
+
+ filename,
+ filename
+
+
+ Prompt for user password, read and decrypt DKEK share and import into SmartCard-HSM.
+ Use to provide a password for decryption rather than prompting for one.
+
+
+
+
+
+ filename,
+ filename
+
+
+ Wrap the key referenced in and save with it together with the key description
+ and certificate to the given file.
+ Use to provide the user PIN on the command line.
+
+
+
+
+
+ filename,
+ filename
+
+
+ Read wrapped key, description and certificate from file and import into SmartCard-HSM
+ under the key reference given in .
+ Determine the key reference using the output of pkcs15-tool -D.
+ Use to provide a user PIN on the command line.
+ Use to remove any key, key description or certificate in the way.
+
+
+
+
+
+ number-of-shares,
+ number-of-shares
+
+
+ Define the number of DKEK shares to use for recreating the DKEK.
+ This is an optional parameter. Using without
+ will disable the DKEK completely.
+ Using with 0 shares requests the SmartCard-HSM to
+ generate a random DKEK. Keys wrapped with this DKEK can only be unwrapped in the
+ same SmartCard-HSM.
+ After using with one or more DKEK shares, the
+ SmartCard-HSM will remain in the initialized state until all DKEK shares have
+ been imported. During this phase no new keys can be generated or imported.
+
+
+
+
+
+ value
+
+
+ Define SO-PIN for initialization.
+
+
+
+
+
+ value
+
+
+ Define user PIN for initialization, wrap or unwrap operation.
+
+
+
+
+
+ value
+
+
+ Define number of PIN retries for user PIN during initialization. Default is 3.
+
+
+
+
+
+ value
+
+
+ Define password for DKEK share encryption.
+
+
+
+
+
+
+
+
+ Force removal of existing key, description and certificate.
+
+
+
+
+
+ num,
+ num
+
+ Use the given reader number. The default is
+ 0, the first reader in the system.
+
+
+
+
+ ,
+
+
+ Wait for a card to be inserted
+
+
+
+
+ ,
+
+
+ Causes sc-hsm-tool to be more verbose.
+ Specify this flag several times to enable debug output in the opensc
+ library.
+
+
+
+
+
+
+ Examples
+ Create a DKEK share:
+ sc-hsm-tool --create-dkek-share dkek-share-1.pbe
+ Initialize SmartCard-HSM to use a single DKEK share
+ sc-hsm-tool --initialize --so-pin 3537363231383830 --pin 648219 --dkek-shares 1
+ Import DKEK share
+ sc-hsm-tool --import-dkek-share dkek-share-1.pbe
+ Wrap referenced key, description and certificate
+ sc-hsm-tool --wrap-key wrap-key.bin --key-reference 1 --pin 648219
+ Unwrap key into same or in different SmartCard-HSM with the same DKEK
+ sc-hsm-tool --unwrap-key wrap-key.bin --key-reference 10 --pin 648219 --force
+
+
+
+ See also
+
+
+ opensc-tool
+ 1
+
+
+
+
+
diff --git a/src/libopensc/asn1.h b/src/libopensc/asn1.h
index a6bbb62a..1189275f 100644
--- a/src/libopensc/asn1.h
+++ b/src/libopensc/asn1.h
@@ -72,6 +72,8 @@ int _sc_asn1_decode(struct sc_context *, struct sc_asn1_entry *,
int _sc_asn1_encode(struct sc_context *, const struct sc_asn1_entry *,
u8 **, size_t *, int);
+int sc_asn1_read_tag(const u8 ** buf, size_t buflen, unsigned int *cla_out,
+ unsigned int *tag_out, size_t *taglen);
const u8 *sc_asn1_find_tag(struct sc_context *ctx, const u8 * buf,
size_t buflen, unsigned int tag, size_t *taglen);
const u8 *sc_asn1_verify_tag(struct sc_context *ctx, const u8 * buf,
diff --git a/src/libopensc/card-sc-hsm.c b/src/libopensc/card-sc-hsm.c
index 516964ad..0a696d4e 100644
--- a/src/libopensc/card-sc-hsm.c
+++ b/src/libopensc/card-sc-hsm.c
@@ -134,11 +134,46 @@ static int sc_hsm_match_card(struct sc_card *card)
+static int sc_hsm_pin_info(sc_card_t *card, struct sc_pin_cmd_data *data,
+ int *tries_left)
+{
+ sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data;
+ sc_apdu_t apdu;
+ int r;
+
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, data->pin_reference);
+
+ r = sc_transmit_apdu(card, &apdu);
+ LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
+
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+
+ if (r == SC_ERROR_PIN_CODE_INCORRECT) {
+ data->pin1.tries_left = apdu.sw2 & 0xF;
+ r = SC_SUCCESS;
+ } else if (r == SC_ERROR_AUTH_METHOD_BLOCKED) {
+ data->pin1.tries_left = 0;
+ r = SC_SUCCESS;
+ }
+ LOG_TEST_RET(card->ctx, r, "Check SW error");
+
+ if (tries_left != NULL) {
+ *tries_left = data->pin1.tries_left;
+ }
+
+ LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
+}
+
+
+
static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data,
int *tries_left)
{
sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data;
+ if (data->cmd == SC_PIN_CMD_GET_INFO) {
+ return sc_hsm_pin_info(card, data, tries_left);
+ }
if (data->pin_reference == 0x88) {
// Save SO PIN for later use in init pin
memcpy(priv->initpw, data->pin1.data, sizeof(priv->initpw));
@@ -581,6 +616,164 @@ static int sc_hsm_get_serialnr(sc_card_t *card, sc_serial_number_t *serial)
+static int sc_hsm_initialize(sc_card_t *card, sc_cardctl_sc_hsm_init_param_t *params)
+{
+ sc_context_t *ctx = card->ctx;
+ int r, i;
+ sc_apdu_t apdu;
+ u8 ibuff[50], *p;
+
+ LOG_FUNC_CALLED(card->ctx);
+
+ p = ibuff;
+ *p++ = 0x80; // Options
+ *p++ = 0x02;
+ memcpy(p, params->options, 2);
+ p += 2;
+
+ *p++ = 0x81; // User PIN
+ *p++ = params->user_pin_len;
+ memcpy(p, params->user_pin, params->user_pin_len);
+ p += params->user_pin_len;
+
+ *p++ = 0x82; // Initialization code
+ *p++ = 0x08;
+ memcpy(p, params->init_code, 8);
+ p += 8;
+
+ *p++ = 0x91; // User PIN retry counter
+ *p++ = 0x01;
+ *p++ = params->user_pin_retry_counter;
+
+ if (params->dkek_shares >= 0) {
+ *p++ = 0x92; // Number of DKEK shares
+ *p++ = 0x01;
+ *p++ = (u8)params->dkek_shares;
+ }
+
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x50, 0x00, 0x00);
+ apdu.cla = 0x80;
+ apdu.data = ibuff;
+ apdu.datalen = p - ibuff;
+ apdu.lc = apdu.datalen;
+
+ r = sc_transmit_apdu(card, &apdu);
+ LOG_TEST_RET(ctx, r, "APDU transmit failed");
+
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+
+ if (r == SC_ERROR_NOT_ALLOWED) {
+ r = SC_ERROR_PIN_CODE_INCORRECT;
+ }
+
+ LOG_TEST_RET(ctx, r, "Check SW error");
+
+ LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
+}
+
+
+
+static int sc_hsm_import_dkek_share(sc_card_t *card, sc_cardctl_sc_hsm_dkek_t *params)
+{
+ sc_context_t *ctx = card->ctx;
+ sc_apdu_t apdu;
+ u8 status[SC_MAX_APDU_BUFFER_SIZE];
+ int r;
+
+ LOG_FUNC_CALLED(card->ctx);
+
+ if (params->importShare) {
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x52, 0x00, 0x00);
+ apdu.cla = 0x80;
+ apdu.data = params->dkek_share;
+ apdu.datalen = sizeof(params->dkek_share);
+ apdu.lc = apdu.datalen;
+ } else {
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x52, 0x00, 0x00);
+ }
+ apdu.cla = 0x80;
+ apdu.le = 0;
+ apdu.resp = status;
+ apdu.resplen = sizeof(status);
+
+ r = sc_transmit_apdu(card, &apdu);
+ LOG_TEST_RET(ctx, r, "APDU transmit failed");
+
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+
+ LOG_TEST_RET(ctx, r, "Check SW error");
+
+ assert(apdu.resplen >= (sizeof(params->key_check_value) + 2));
+
+ params->dkek_shares = status[0];
+ params->outstanding_shares = status[1];
+ memcpy(params->key_check_value, status + 2, sizeof(params->key_check_value));
+
+ LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
+}
+
+
+
+static int sc_hsm_wrap_key(sc_card_t *card, sc_cardctl_sc_hsm_wrapped_key_t *params)
+{
+ sc_context_t *ctx = card->ctx;
+ sc_apdu_t apdu;
+ u8 data[SC_MAX_EXT_APDU_BUFFER_SIZE];
+ int r;
+
+ LOG_FUNC_CALLED(card->ctx);
+
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_EXT, 0x72, params->key_id, 0x92);
+ apdu.cla = 0x80;
+ apdu.le = 0;
+ apdu.resp = data;
+ apdu.resplen = sizeof(data);
+
+ r = sc_transmit_apdu(card, &apdu);
+ LOG_TEST_RET(ctx, r, "APDU transmit failed");
+
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+
+ LOG_TEST_RET(ctx, r, "Check SW error");
+
+ params->wrapped_key_length = apdu.resplen;
+ params->wrapped_key = malloc(apdu.resplen);
+ if (params->wrapped_key == NULL) {
+ LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
+ }
+ memcpy(params->wrapped_key, data, apdu.resplen);
+ LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
+}
+
+
+
+static int sc_hsm_unwrap_key(sc_card_t *card, sc_cardctl_sc_hsm_wrapped_key_t *params)
+{
+ sc_context_t *ctx = card->ctx;
+ sc_apdu_t apdu;
+ u8 status[MAX_EXT_APDU_LENGTH];
+ int r;
+
+ LOG_FUNC_CALLED(card->ctx);
+
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_EXT, 0x74, params->key_id, 0x93);
+ apdu.cla = 0x80;
+ apdu.lc = params->wrapped_key_length;
+ apdu.data = params->wrapped_key;
+ apdu.datalen = params->wrapped_key_length;
+
+ r = sc_transmit_apdu(card, &apdu);
+ LOG_TEST_RET(ctx, r, "APDU transmit failed");
+
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+
+ LOG_TEST_RET(ctx, r, "Check SW error");
+
+ LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
+}
+
+
+
static int sc_hsm_init_token(sc_card_t *card, sc_cardctl_pkcs11_init_token_t *params)
{
sc_context_t *ctx = card->ctx;
@@ -734,6 +927,14 @@ static int sc_hsm_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
return sc_hsm_init_pin(card, (sc_cardctl_pkcs11_init_pin_t *)ptr);
case SC_CARDCTL_SC_HSM_GENERATE_KEY:
return sc_hsm_generate_keypair(card, (sc_cardctl_sc_hsm_keygen_info_t *)ptr);
+ case SC_CARDCTL_SC_HSM_INITIALIZE:
+ return sc_hsm_initialize(card, (sc_cardctl_sc_hsm_init_param_t *)ptr);
+ case SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE:
+ return sc_hsm_import_dkek_share(card, (sc_cardctl_sc_hsm_dkek_t *)ptr);
+ case SC_CARDCTL_SC_HSM_WRAP_KEY:
+ return sc_hsm_wrap_key(card, (sc_cardctl_sc_hsm_wrapped_key_t *)ptr);
+ case SC_CARDCTL_SC_HSM_UNWRAP_KEY:
+ return sc_hsm_unwrap_key(card, (sc_cardctl_sc_hsm_wrapped_key_t *)ptr);
}
return SC_ERROR_NOT_SUPPORTED;
}
diff --git a/src/libopensc/card-sc-hsm.h b/src/libopensc/card-sc-hsm.h
index 370641bf..0d01b54b 100644
--- a/src/libopensc/card-sc-hsm.h
+++ b/src/libopensc/card-sc-hsm.h
@@ -47,6 +47,8 @@
#define ALGO_EC_SHA256 0x73 /* ECDSA signature with SHA-256 hash */
#define ALGO_EC_DH 0x80 /* ECDH key derivation */
+#define ID_USER_PIN 0x81 /* User PIN identifier */
+#define ID_SO_PIN 0x88 /* Security officer PIN identifier */
/* Information the driver maintains between calls */
typedef struct sc_hsm_private_data {
diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h
index caa23989..e32ad8c1 100644
--- a/src/libopensc/cardctl.h
+++ b/src/libopensc/cardctl.h
@@ -244,7 +244,11 @@ enum {
* SmartCard-HSM
*/
SC_CARDCTL_SC_HSMP_BASE = _CTL_PREFIX('S', 'C', 'H'),
- SC_CARDCTL_SC_HSM_GENERATE_KEY
+ SC_CARDCTL_SC_HSM_GENERATE_KEY,
+ SC_CARDCTL_SC_HSM_INITIALIZE,
+ SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE,
+ SC_CARDCTL_SC_HSM_WRAP_KEY,
+ SC_CARDCTL_SC_HSM_UNWRAP_KEY
};
enum {
@@ -908,6 +912,28 @@ typedef struct sc_cardctl_sc_hsm_keygen_info {
size_t gakpresponse_len; /* Size of response */
} sc_cardctl_sc_hsm_keygen_info_t;
+typedef struct sc_cardctl_sc_hsm_init_param {
+ u8 init_code[8]; /* Initialization code */
+ u8 *user_pin; /* Initial user PIN */
+ size_t user_pin_len; /* Length of user PIN */
+ u8 user_pin_retry_counter; /* Retry counter default value */
+ u8 options[2]; /* Initilization options */
+ char dkek_shares; /* Number of DKEK shares, 0 for card generated, -1 for none */
+} sc_cardctl_sc_hsm_init_param_t;
+
+typedef struct sc_cardctl_sc_hsm_dkek {
+ int importShare; /* True to import share, false to just query status */
+ u8 dkek_share[32]; /* AES-256 DKEK share */
+ u8 dkek_shares; /* Total number of shares */
+ u8 outstanding_shares; /* Number of shares to be presented */
+ u8 key_check_value[8]; /* Key check value for DKEK */
+} sc_cardctl_sc_hsm_dkek_t;
+
+typedef struct sc_cardctl_sc_hsm_wrapped_key {
+ u8 key_id; /* Key identifier */
+ u8 *wrapped_key; /* Binary wrapped key */
+ size_t wrapped_key_length; /* Length of key blob */
+} sc_cardctl_sc_hsm_wrapped_key_t;
#ifdef __cplusplus
}
diff --git a/src/libopensc/errors.c b/src/libopensc/errors.c
index be5a27f5..aee5fa8b 100644
--- a/src/libopensc/errors.c
+++ b/src/libopensc/errors.c
@@ -69,7 +69,8 @@ const char *sc_strerror(int error)
"Data object not found",
"Not enough memory on card",
"Part of returned data may be corrupted",
- "End of file/record reached before reading Le bytes"
+ "End of file/record reached before reading Le bytes",
+ "Reference data not usable"
};
const int card_base = -SC_ERROR_CARD_CMD_FAILED;
diff --git a/src/libopensc/errors.h b/src/libopensc/errors.h
index 8e5e62a0..579de685 100644
--- a/src/libopensc/errors.h
+++ b/src/libopensc/errors.h
@@ -67,6 +67,7 @@ extern "C" {
#define SC_ERROR_NOT_ENOUGH_MEMORY -1217
#define SC_ERROR_CORRUPTED_DATA -1218
#define SC_ERROR_FILE_END_REACHED -1219
+#define SC_ERROR_REF_DATA_NOT_USABLE -1220
/* Returned by OpenSC library when called with invalid arguments */
#define SC_ERROR_INVALID_ARGUMENTS -1300
diff --git a/src/libopensc/internal.h b/src/libopensc/internal.h
index 85402ad9..f0270bea 100644
--- a/src/libopensc/internal.h
+++ b/src/libopensc/internal.h
@@ -123,9 +123,6 @@ int _sc_card_add_rsa_alg(struct sc_card *card, unsigned int key_length,
int _sc_card_add_ec_alg(struct sc_card *card, unsigned int key_length,
unsigned long flags, unsigned long ext_flags);
-int sc_asn1_read_tag(const u8 ** buf, size_t buflen, unsigned int *cla_out,
- unsigned int *tag_out, size_t *taglen);
-
/********************************************************************/
/* pkcs1 padding/encoding functions */
/********************************************************************/
diff --git a/src/libopensc/iso7816.c b/src/libopensc/iso7816.c
index 89c644f4..a15addf6 100644
--- a/src/libopensc/iso7816.c
+++ b/src/libopensc/iso7816.c
@@ -51,7 +51,7 @@ static const struct sc_card_error iso7816_errors[] = {
{ 0x6981, SC_ERROR_CARD_CMD_FAILED, "Command incompatible with file structure" },
{ 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED, "Security status not satisfied" },
{ 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "Authentication method blocked" },
- { 0x6984, SC_ERROR_CARD_CMD_FAILED, "Referenced data invalidated" },
+ { 0x6984, SC_ERROR_REF_DATA_NOT_USABLE, "Referenced data not usable" },
{ 0x6985, SC_ERROR_NOT_ALLOWED, "Conditions of use not satisfied" },
{ 0x6986, SC_ERROR_NOT_ALLOWED, "Command not allowed (no current EF)" },
{ 0x6987, SC_ERROR_INCORRECT_PARAMETERS,"Expected SM data objects missing" },
diff --git a/src/libopensc/libopensc.exports b/src/libopensc/libopensc.exports
index 0f161745..fe118014 100644
--- a/src/libopensc/libopensc.exports
+++ b/src/libopensc/libopensc.exports
@@ -44,6 +44,7 @@ sc_asn1_decode_object_id
sc_asn1_encode
sc_asn1_encode_object_id
sc_asn1_encode_algorithm_id
+sc_asn1_read_tag
sc_asn1_find_tag
sc_asn1_print_tags
sc_asn1_put_tag
diff --git a/src/pkcs15init/pkcs15-sc-hsm.c b/src/pkcs15init/pkcs15-sc-hsm.c
index 18cfb40c..389a5d3d 100644
--- a/src/pkcs15init/pkcs15-sc-hsm.c
+++ b/src/pkcs15init/pkcs15-sc-hsm.c
@@ -180,7 +180,7 @@ static int sc_hsm_update_ef(sc_pkcs15_card_t *p15card, u8 prefix, u8 id, int era
file->status = SC_FILE_STATUS_ACTIVATED;
r = sc_create_file(card, file);
sc_file_free(file);
- LOG_TEST_RET(card->ctx, r, "Could not creat file");
+ LOG_TEST_RET(card->ctx, r, "Could not create file");
}
r = sc_update_binary(card, 0, buf, buflen, 0);
@@ -462,8 +462,8 @@ static int sc_hsm_generate_key(struct sc_profile *profile, struct sc_pkcs15_card
cvcpo = cvcbin;
- sc_asn1_read_tag(&cvcpo, cvclen, &cla, &tag, &taglen);
- sc_asn1_read_tag(&cvcpo, cvclen, &cla, &tag, &taglen);
+ sc_asn1_read_tag((const u8 **)&cvcpo, cvclen, &cla, &tag, &taglen);
+ sc_asn1_read_tag((const u8 **)&cvcpo, cvclen, &cla, &tag, &taglen);
sc_hsm_keyinfo.key_id = key_info->key_reference;
sc_hsm_keyinfo.auth_key_id = 0;
diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am
index b42f8a4d..a1e37344 100644
--- a/src/tools/Makefile.am
+++ b/src/tools/Makefile.am
@@ -7,7 +7,7 @@ noinst_HEADERS = util.h
bin_PROGRAMS = opensc-tool opensc-explorer pkcs15-tool pkcs15-crypt \
pkcs11-tool cardos-tool eidenv openpgp-tool iasecc-tool
if ENABLE_OPENSSL
-bin_PROGRAMS += cryptoflex-tool pkcs15-init netkey-tool piv-tool westcos-tool
+bin_PROGRAMS += cryptoflex-tool pkcs15-init netkey-tool piv-tool westcos-tool sc-hsm-tool
endif
# compile with $(PTHREAD_CFLAGS) to allow debugging with gdb
@@ -46,6 +46,7 @@ openpgp_tool_SOURCES = openpgp-tool.c util.c
openpgp_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS)
iasecc_tool_SOURCES = iasecc-tool.c util.c
iasecc_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS)
+sc_hsm_tool_SOURCES = sc-hsm-tool.c util.c
if WIN32
opensc_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc
@@ -62,4 +63,5 @@ netkey_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc
westcos_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc
openpgp_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc
iasecc_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc
+sc_hsm_tool_SOURCES += $(top_builddir)/win32/versioninfo.rc
endif
diff --git a/src/tools/Makefile.mak b/src/tools/Makefile.mak
index 153bff76..3706b6af 100644
--- a/src/tools/Makefile.mak
+++ b/src/tools/Makefile.mak
@@ -3,7 +3,7 @@ TOPDIR = ..\..
!INCLUDE $(TOPDIR)\win32\Make.rules.mak
TARGETS = opensc-tool.exe opensc-explorer.exe pkcs15-tool.exe pkcs15-crypt.exe \
- pkcs11-tool.exe cardos-tool.exe eidenv.exe \
+ pkcs11-tool.exe cardos-tool.exe eidenv.exe sc-hsm-tool.exe \
$(PROGRAMS_OPENSSL)
$(TARGETS): $(TOPDIR)\win32\versioninfo.res util.obj
diff --git a/src/tools/sc-hsm-tool.c b/src/tools/sc-hsm-tool.c
new file mode 100644
index 00000000..72613846
--- /dev/null
+++ b/src/tools/sc-hsm-tool.c
@@ -0,0 +1,913 @@
+/*
+ * sc-hsm-tool.c: SmartCard-HSM Management Tool
+ *
+ * Copyright (C) 2001 Juha Yrjölä
+ * Copyright (C) 2012 www.CardContact.de, Andreas Schwier, Minden, Germany
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include
+#include
+#ifdef HAVE_UNISTD_H
+#include
+#endif
+#include
+#include
+#include
+#include
+
+/* Requires openssl for dkek import */
+#include
+#include
+#include
+
+
+#include "libopensc/opensc.h"
+#include "libopensc/cardctl.h"
+#include "libopensc/asn1.h"
+#include "libopensc/card-sc-hsm.h"
+#include "util.h"
+
+static const char *app_name = "sc-hsm-tool";
+
+static const char magic[] = "Salted__";
+
+static struct sc_aid sc_hsm_aid = { { 0xE8,0x2B,0x06,0x01,0x04,0x01,0x81,0xC3,0x1F,0x02,0x01 }, 11 };
+
+static int opt_wait = 0;
+static char *opt_reader;
+static int verbose = 0;
+
+// Some reasonable maximums
+#define MAX_CERT 4096
+#define MAX_PRKD 256
+#define MAX_KEY 512
+#define MAX_WRAPPED_KEY (MAX_CERT + MAX_PRKD + MAX_KEY)
+
+enum {
+ OPT_SO_PIN = 0x100,
+ OPT_PIN,
+ OPT_RETRY,
+ OPT_PASSWORD
+};
+
+static const struct option options[] = {
+ { "initialize", 0, NULL, 'X' },
+ { "create-dkek-share", 1, NULL, 'C' },
+ { "import-dkek-share", 1, NULL, 'I' },
+ { "wrap-key", 1, NULL, 'W' },
+ { "unwrap-key", 1, NULL, 'U' },
+ { "dkek-shares", 1, NULL, 's' },
+ { "so-pin", 1, NULL, OPT_SO_PIN },
+ { "pin", 1, NULL, OPT_PIN },
+ { "pin-retry", 1, NULL, OPT_RETRY },
+ { "password", 1, NULL, OPT_PASSWORD },
+ { "key-reference", 1, NULL, 'i' },
+ { "force", 0, NULL, 'f' },
+ { "reader", 1, NULL, 'r' },
+ { "wait", 0, NULL, 'w' },
+ { "verbose", 0, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
+};
+
+static const char *option_help[] = {
+ "Initialize token",
+ "Create DKEK key share and save to ",
+ "Import DKEK key share ",
+ "Wrap key and save to ",
+ "Unwrap key read from ",
+ "Number of DKEK shares [No DKEK]",
+ "Define security officer PIN (SO-PIN)",
+ "Define user PIN",
+ "Define user PIN retry counter",
+ "Define password for DKEK share",
+ "Key reference for key wrap/unwrap",
+ "Force replacement of key and certificate",
+ "Uses reader number [0]",
+ "Wait for a card to be inserted",
+ "Verbose operation. Use several times to enable debug output.",
+};
+
+
+static sc_context_t *ctx = NULL;
+static sc_card_t *card = NULL;
+
+
+
+
+static void print_dkek_info(sc_cardctl_sc_hsm_dkek_t *dkekinfo) {
+ printf("DKEK shares : %d\n", dkekinfo->dkek_shares);
+ if (dkekinfo->outstanding_shares > 0) {
+ printf("DKEK import pending, %d share(s) still missing\n",dkekinfo->outstanding_shares);
+ } else {
+ printf("DKEK key check value : ");
+ util_hex_dump(stdout, dkekinfo->key_check_value, 8, NULL);
+ printf("\n");
+ }
+}
+
+
+
+static void print_info(sc_card_t *card, sc_file_t *file)
+{
+ int r, tries_left;
+ struct sc_pin_cmd_data data;
+ sc_cardctl_sc_hsm_dkek_t dkekinfo;
+
+ u8 major, minor;
+
+ major = file->prop_attr[file->prop_attr_len - 2];
+ minor = file->prop_attr[file->prop_attr_len - 1];
+ printf("Version : %d.%d\n", (int)major, (int)minor);
+
+ /* Try to update PIN info from card */
+ memset(&data, 0, sizeof(data));
+ data.cmd = SC_PIN_CMD_GET_INFO;
+ data.pin_type = SC_AC_CHV;
+ data.pin_reference = ID_USER_PIN;
+
+ r = sc_pin_cmd(card, &data, &tries_left);
+
+ if (r == SC_ERROR_REF_DATA_NOT_USABLE) {
+ printf("SmartCard-HSM has never been initialized\n");
+ } else {
+ if (tries_left == 0) {
+ printf("User PIN locked\n");
+ } else {
+ printf("User PIN tries left : %d\n", tries_left);
+ }
+ }
+
+ memset(&dkekinfo, 0, sizeof(dkekinfo));
+
+ r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, (void *)&dkekinfo);
+
+ if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares
+ return;
+ }
+
+ if (r < 0) {
+ fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, *) failed with %s\n", sc_strerror(r));
+ }
+ print_dkek_info(&dkekinfo);
+}
+
+
+
+static void initialize(sc_card_t *card, const char *so_pin, const char *user_pin, int retry_counter, int dkek_shares)
+{
+ sc_cardctl_sc_hsm_init_param_t param;
+ size_t len;
+ char *_so_pin = NULL, *_user_pin = NULL;
+ int r;
+
+ if (so_pin == NULL) {
+ printf("Enter SO-PIN : ");
+ util_getpass(&_so_pin, NULL, stdin);
+ printf("\n");
+ } else {
+ _so_pin = (char *)so_pin;
+ }
+
+ if (user_pin == NULL) {
+ printf("Enter initial User-PIN : ");
+ util_getpass(&_user_pin, NULL, stdin);
+ printf("\n");
+ } else {
+ _user_pin = (char *)user_pin;
+ }
+
+ len = sizeof(param.init_code);
+ r = sc_hex_to_bin(_so_pin, param.init_code, &len);
+ if (r < 0) {
+ fprintf(stderr, "Error decoding initialization code (%s)\n", sc_strerror(r));
+ return;
+ }
+
+ if (len != 8) {
+ fprintf(stderr, "Initialization code must contain 8 bytes\n");
+ return;
+ }
+
+ param.user_pin_len = strlen(_user_pin);
+ param.user_pin = (u8 *)_user_pin;
+
+ param.user_pin_retry_counter = (u8)retry_counter;
+
+ param.options[0] = 0x00;
+ param.options[1] = 0x01;
+
+ param.dkek_shares = (char)dkek_shares;
+
+ r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_INITIALIZE, (void *)¶m);
+ if (r < 0) {
+ fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_INITIALIZE, *) failed with %s\n", sc_strerror(r));
+ }
+}
+
+
+
+static void import_dkek_share(sc_card_t *card, const char *inf, int iter, char *password)
+{
+ sc_cardctl_sc_hsm_dkek_t dkekinfo;
+ EVP_CIPHER_CTX ctx;
+ FILE *in = NULL;
+ u8 filebuff[64],key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH],outbuff[64];
+ char *pwd = NULL;
+ int r, outlen;
+
+ in = fopen(inf, "rb");
+
+ if (in == NULL) {
+ perror(inf);
+ return;
+ }
+
+ if (fread(filebuff, 1, sizeof(filebuff), in) != sizeof(filebuff)) {
+ perror(inf);
+ return;
+ }
+
+ fclose(in);
+
+ if (memcmp(filebuff, magic, sizeof(magic) - 1)) {
+ printf("File %s is not a DKEK share\n", inf);
+ return;
+ }
+
+ if (password == NULL) {
+ printf("Enter password to decrypt DKEK share : ");
+ util_getpass(&pwd, NULL, stdin);
+ printf("\n");
+ } else {
+ pwd = password;
+ }
+
+ printf("Deciphering DKEK share, please wait...\n");
+ EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, strlen(pwd), iter, key, iv);
+ OPENSSL_cleanse(pwd, strlen(pwd));
+
+ if (password == NULL) {
+ free(pwd);
+ }
+
+ EVP_CIPHER_CTX_init(&ctx);
+ EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv);
+ if (!EVP_DecryptUpdate(&ctx, outbuff, &outlen, filebuff + 16, sizeof(filebuff) - 16)) {
+ printf("Error decrypting DKEK share. Password correct ?\n");
+ return;
+ }
+
+ if (!EVP_DecryptFinal_ex(&ctx, outbuff + outlen, &r)) {
+ printf("Error decrypting DKEK share. Password correct ?\n");
+ return;
+ }
+
+ memset(&dkekinfo, 0, sizeof(dkekinfo));
+ memcpy(dkekinfo.dkek_share, outbuff, sizeof(dkekinfo.dkek_share));
+ dkekinfo.importShare = 1;
+
+ OPENSSL_cleanse(outbuff, sizeof(outbuff));
+
+ r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, (void *)&dkekinfo);
+
+ OPENSSL_cleanse(&dkekinfo.dkek_share, sizeof(dkekinfo.dkek_share));
+ EVP_CIPHER_CTX_cleanup(&ctx);
+
+ if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares
+ return;
+ }
+
+ if (r < 0) {
+ fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_IMPORT_DKEK_SHARE, *) failed with %s\n", sc_strerror(r));
+ return;
+ }
+ printf("DKEK share imported\n");
+ print_dkek_info(&dkekinfo);
+}
+
+
+
+static void create_dkek_share(sc_card_t *card, const char *outf, int iter, char *password)
+{
+ EVP_CIPHER_CTX ctx;
+ FILE *out = NULL;
+ u8 filebuff[64],key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH],outbuff[64];
+ u8 dkek_share[32];
+ char *pwd = NULL;
+ int r, outlen;
+
+ if (password == NULL) {
+ char *refpwd = NULL;
+
+ printf("\nThe DKEK share will be enciphered using a key derived from a user supplied password.\n");
+ printf("The security of the DKEK share relies on a well chosen and sufficiently long password.\n");
+ printf("The recommended length is more than 10 characters, which are mixed letters, numbers and\n");
+ printf("symbols.\n\n");
+ printf("Please keep the generated DKEK share file in a save location. We also recommend to keep a\n");
+ printf("paper printout, in case the electronic version becomes unavailable. A printable version\n");
+ printf("of the file can be generated using \"openssl base64 -in \".\n");
+ while(1) {
+ printf("Enter password to encrypt DKEK share : ");
+ util_getpass(&pwd, NULL, stdin);
+ printf("\n");
+ if (strlen(pwd) < 6) {
+ printf("Password way to short. Please retry.\n");
+ continue;
+ }
+ printf("Please retype password to confirm : ");
+ util_getpass(&refpwd, NULL, stdin);
+ printf("\n");
+ if (strcmp(pwd, refpwd)) {
+ printf("Passwords do not match. Please retry.\n");
+ continue;
+ }
+ break;
+ }
+ OPENSSL_cleanse(refpwd, strlen(refpwd));
+ free(refpwd);
+ } else {
+ pwd = password;
+ }
+
+ memcpy(filebuff, magic, sizeof(magic) - 1);
+
+ r = sc_get_challenge(card, filebuff + 8, 8);
+ if (r < 0) {
+ printf("Error generating random number failed with ", sc_strerror(r));
+ return;
+ }
+
+ printf("Enciphering DKEK share, please wait...\n");
+ EVP_BytesToKey(EVP_aes_256_cbc(), EVP_md5(), filebuff + 8, (u8 *)pwd, strlen(pwd), iter, key, iv);
+
+ if (password == NULL) {
+ OPENSSL_cleanse(pwd, strlen(pwd));
+ free(pwd);
+ }
+
+ r = sc_get_challenge(card, dkek_share, sizeof(dkek_share));
+ if (r < 0) {
+ printf("Error generating random number failed with ", sc_strerror(r));
+ return;
+ }
+
+ EVP_CIPHER_CTX_init(&ctx);
+ EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv);
+ if (!EVP_EncryptUpdate(&ctx, filebuff + 16, &outlen, dkek_share, sizeof(dkek_share))) {
+ printf("Error encrypting DKEK share\n");
+ return;
+ }
+
+ if (!EVP_EncryptFinal_ex(&ctx, filebuff + 16 + outlen, &r)) {
+ printf("Error encrypting DKEK share\n");
+ return;
+ }
+
+ out = fopen(outf, "wb");
+
+ if (out == NULL) {
+ perror(outf);
+ return;
+ }
+
+ if (fwrite(filebuff, 1, sizeof(filebuff), out) != sizeof(filebuff)) {
+ perror(outf);
+ return;
+ }
+
+ fclose(out);
+
+ OPENSSL_cleanse(filebuff, sizeof(filebuff));
+ EVP_CIPHER_CTX_cleanup(&ctx);
+
+ printf("DKEK share created and saved to %s\n", outf);
+}
+
+
+
+static size_t determineLength(const u8 *tlv, size_t buflen)
+{
+ const u8 *ptr = tlv;
+ unsigned int cla,tag;
+ size_t len;
+
+ if (sc_asn1_read_tag(&ptr, buflen, &cla, &tag, &len) != SC_SUCCESS) {
+ return 0;
+ }
+
+ return len + (ptr - tlv);
+}
+
+
+
+static void wrap_key(sc_card_t *card, u8 keyid, const char *outf, const char *pin)
+{
+ sc_cardctl_sc_hsm_wrapped_key_t wrapped_key;
+ struct sc_pin_cmd_data data;
+ sc_file_t *file = NULL;
+ sc_path_t path;
+ FILE *out = NULL;
+ u8 fid[2];
+ u8 ef_prkd[MAX_PRKD];
+ u8 ef_cert[MAX_CERT];
+ u8 keyblob[MAX_WRAPPED_KEY];
+ u8 *key;
+ u8 *ptr;
+ char *lpin = NULL;
+ size_t key_len;
+ int r, ef_prkd_len, ef_cert_len;
+
+ if (pin == NULL) {
+ printf("Enter User PIN : ");
+ util_getpass(&lpin, NULL, stdin);
+ printf("\n");
+ } else {
+ lpin = (u8 *)pin;
+ }
+
+ memset(&data, 0, sizeof(data));
+ data.cmd = SC_PIN_CMD_VERIFY;
+ data.pin_type = SC_AC_CHV;
+ data.pin_reference = ID_USER_PIN;
+ data.pin1.data = lpin;
+ data.pin1.len = strlen(lpin);
+
+ r = sc_pin_cmd(card, &data, NULL);
+
+ if (r < 0) {
+ fprintf(stderr, "PIN verification failed with %s\n", sc_strerror(r));
+ return;
+ }
+
+ if (pin == NULL) {
+ free(lpin);
+ }
+
+ wrapped_key.key_id = keyid;
+
+ r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_WRAP_KEY, (void *)&wrapped_key);
+
+ if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares
+ return;
+ }
+
+ if (r < 0) {
+ fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_WRAP_KEY, *) failed with %s\n", sc_strerror(r));
+ return;
+ }
+
+
+ fid[0] = PRKD_PREFIX;
+ fid[1] = keyid;
+ ef_prkd_len = 0;
+
+ /* Try to select a related EF containing the PKCS#15 description of the key */
+ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0);
+ r = sc_select_file(card, &path, NULL);
+
+ if (r == SC_SUCCESS) {
+ ef_prkd_len = sc_read_binary(card, 0, ef_prkd, sizeof(ef_prkd), 0);
+
+ if (ef_prkd_len < 0) {
+ fprintf(stderr, "Error reading PRKD file %s. Skipping.\n", sc_strerror(ef_prkd_len));
+ ef_prkd_len = 0;
+ } else {
+ ef_prkd_len = determineLength(ef_prkd, ef_prkd_len);
+ }
+ }
+
+ fid[0] = EE_CERTIFICATE_PREFIX;
+ fid[1] = keyid;
+ ef_cert_len = 0;
+
+ /* Try to select a related EF containing the certificate for the key */
+ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0);
+ r = sc_select_file(card, &path, NULL);
+
+ if (r == SC_SUCCESS) {
+ ef_cert_len = sc_read_binary(card, 0, ef_cert, sizeof(ef_cert), 0);
+
+ if (ef_cert_len < 0) {
+ fprintf(stderr, "Error reading certificate %s. Skipping\n", sc_strerror(ef_cert_len));
+ ef_cert_len = 0;
+ } else {
+ ef_cert_len = determineLength(ef_cert, ef_cert_len);
+ }
+ }
+
+
+ ptr = keyblob;
+
+ // Encode key in octet string object
+ sc_asn1_write_element(card->ctx, SC_ASN1_OCTET_STRING,
+ wrapped_key.wrapped_key, wrapped_key.wrapped_key_length,
+ &key, &key_len);
+
+ memcpy(ptr, key, key_len);
+ ptr += key_len;
+ free(key);
+
+ // Add private key description
+ if (ef_prkd_len > 0) {
+ memcpy(ptr, ef_prkd, ef_prkd_len);
+ ptr += ef_prkd_len;
+ }
+
+ // Add certificate
+ if (ef_cert_len > 0) {
+ memcpy(ptr, ef_cert, ef_cert_len);
+ ptr += ef_cert_len;
+ }
+
+ // Encode key in octet string object
+ sc_asn1_write_element(card->ctx, SC_ASN1_SEQUENCE|SC_ASN1_CONS,
+ keyblob, ptr - keyblob,
+ &key, &key_len);
+
+ out = fopen(outf, "wb");
+
+ if (out == NULL) {
+ perror(outf);
+ free(key);
+ return;
+ }
+
+ if (fwrite(key, 1, key_len, out) != key_len) {
+ perror(outf);
+ free(key);
+ return;
+ }
+
+ free(key);
+ fclose(out);
+}
+
+
+
+static int update_ef(sc_card_t *card, u8 prefix, u8 id, int erase, const u8 *buf, size_t buflen)
+{
+ sc_file_t *file = NULL;
+ sc_file_t newfile;
+ sc_path_t path;
+ u8 fid[2];
+ int r;
+
+ fid[0] = prefix;
+ fid[1] = id;
+
+ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, -1);
+
+ r = sc_select_file(card, &path, NULL);
+
+ if ((r == SC_SUCCESS) && erase) {
+ r = sc_delete_file(card, &path);
+ r = SC_ERROR_FILE_NOT_FOUND;
+ }
+
+ if (r == SC_ERROR_FILE_NOT_FOUND) {
+ file = sc_file_new();
+ file->id = (path.value[0] << 8) | path.value[1];
+ file->type = SC_FILE_TYPE_WORKING_EF;
+ file->ef_structure = SC_FILE_EF_TRANSPARENT;
+ file->size = (size_t) 0;
+ file->status = SC_FILE_STATUS_ACTIVATED;
+ r = sc_create_file(card, file);
+ sc_file_free(file);
+ if (r < 0) {
+ return r;
+ }
+ }
+
+ r = sc_update_binary(card, 0, buf, buflen, 0);
+ return r;
+}
+
+
+
+static void unwrap_key(sc_card_t *card, u8 keyid, const char *inf, const char *pin, int force)
+{
+ sc_cardctl_sc_hsm_wrapped_key_t wrapped_key;
+ struct sc_pin_cmd_data data;
+ u8 keyblob[MAX_WRAPPED_KEY];
+ const u8 *ptr,*prkd,*cert;
+ FILE *in = NULL;
+ sc_path_t path;
+ u8 fid[2];
+ char *lpin = NULL;
+ unsigned int cla, tag;
+ int r, keybloblen;
+ size_t len, olen, prkd_len, cert_len;
+
+ in = fopen(inf, "rb");
+
+ if (in == NULL) {
+ perror(inf);
+ return;
+ }
+
+ if ((keybloblen = fread(keyblob, 1, sizeof(keyblob), in)) < 0) {
+ perror(inf);
+ return;
+ }
+
+ fclose(in);
+
+ ptr = keyblob;
+ if ((sc_asn1_read_tag(&ptr, keybloblen, &cla, &tag, &len) != SC_SUCCESS) ||
+ ((cla & SC_ASN1_TAG_CONSTRUCTED) != SC_ASN1_TAG_CONSTRUCTED) ||
+ ((tag != SC_ASN1_TAG_SEQUENCE)) ){
+ fprintf(stderr, "Invalid wrapped key format (Outer sequence).\n");
+ return;
+ }
+
+ if ((sc_asn1_read_tag(&ptr, len, &cla, &tag, &olen) != SC_SUCCESS) ||
+ (cla & SC_ASN1_TAG_CONSTRUCTED) ||
+ ((tag != SC_ASN1_TAG_OCTET_STRING)) ){
+ fprintf(stderr, "Invalid wrapped key format (Key binary).\n");
+ return;
+ }
+
+ wrapped_key.wrapped_key = (u8 *)ptr;
+ wrapped_key.wrapped_key_length = olen;
+
+ ptr += olen;
+ prkd = ptr;
+ prkd_len = determineLength(ptr, keybloblen - (ptr - keyblob));
+
+ ptr += prkd_len;
+ cert = ptr;
+ cert_len = determineLength(ptr, keybloblen - (ptr - keyblob));
+
+ printf("Wrapped key contains:\n");
+ printf(" Key blob\n");
+ if (prkd_len > 0) {
+ printf(" Private Key Description (PRKD)\n");
+ }
+ if (cert_len > 0) {
+ printf(" Certificate\n");
+ }
+
+ if ((prkd_len > 0) && !force) {
+ fid[0] = PRKD_PREFIX;
+ fid[1] = keyid;
+
+ /* Try to select a related EF containing the PKCS#15 description of the key */
+ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0);
+ r = sc_select_file(card, &path, NULL);
+
+ if (r == SC_SUCCESS) {
+ fprintf(stderr, "Found existing private key description in EF with fid %02x%02x. Please remove key first, select unused key reference or use --force.\n", fid[0], fid[1]);
+ return;
+ }
+ }
+
+ if ((cert_len > 0) && !force) {
+ fid[0] = EE_CERTIFICATE_PREFIX;
+ fid[1] = keyid;
+
+ /* Try to select a related EF containing the certificate */
+ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0);
+ r = sc_select_file(card, &path, NULL);
+
+ if (r == SC_SUCCESS) {
+ fprintf(stderr, "Found existing certificate in EF with fid %02x%02x. Please remove certificate first, select unused key reference or use --force.\n", fid[0], fid[1]);
+ return;
+ }
+ }
+
+ if (pin == NULL) {
+ printf("Enter User PIN : ");
+ util_getpass(&lpin, NULL, stdin);
+ printf("\n");
+ } else {
+ lpin = (u8 *)pin;
+ }
+
+ memset(&data, 0, sizeof(data));
+ data.cmd = SC_PIN_CMD_VERIFY;
+ data.pin_type = SC_AC_CHV;
+ data.pin_reference = ID_USER_PIN;
+ data.pin1.data = lpin;
+ data.pin1.len = strlen(lpin);
+
+ r = sc_pin_cmd(card, &data, NULL);
+
+ if (r < 0) {
+ fprintf(stderr, "PIN verification failed with %s\n", sc_strerror(r));
+ return;
+ }
+
+ if (pin == NULL) {
+ free(lpin);
+ }
+
+ if (force) {
+ fid[0] = KEY_PREFIX;
+ fid[1] = keyid;
+
+ sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, 2, 0, -1);
+ sc_delete_file(card, &path);
+ }
+
+ wrapped_key.key_id = keyid;
+
+ r = sc_card_ctl(card, SC_CARDCTL_SC_HSM_UNWRAP_KEY, (void *)&wrapped_key);
+
+ if (r == SC_ERROR_INS_NOT_SUPPORTED) { // Not supported or not initialized for key shares
+ return;
+ }
+
+ if (r < 0) {
+ fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_SC_HSM_UNWRAP_KEY, *) failed with %s\n", sc_strerror(r));
+ return;
+ }
+
+ if (prkd_len > 0) {
+ r = update_ef(card, PRKD_PREFIX, keyid, force, prkd, prkd_len);
+
+ if (r < 0) {
+ fprintf(stderr, "Updating private key description failed with %s\n", sc_strerror(r));
+ return;
+ }
+ }
+
+ if (cert_len > 0) {
+ r = update_ef(card, EE_CERTIFICATE_PREFIX, keyid, force, cert, cert_len);
+
+ if (r < 0) {
+ fprintf(stderr, "Updating certificate failed with %s\n", sc_strerror(r));
+ return;
+ }
+ }
+
+ printf("Key successfully imported\n");
+}
+
+
+
+int main(int argc, char * const argv[])
+{
+ int err = 0, r, c, long_optind = 0;
+ int action_count = 0;
+ int do_initialize = 0;
+ int do_import_dkek_share = 0;
+ int do_create_dkek_share = 0;
+ int do_wrap_key = 0;
+ int do_unwrap_key = 0;
+ sc_path_t path;
+ sc_file_t *file = NULL;
+ const char *opt_so_pin = NULL;
+ const char *opt_pin = NULL;
+ const char *opt_filename = NULL;
+ char *opt_password = NULL;
+ int opt_retry_counter = 3;
+ int opt_dkek_shares = -1;
+ int opt_key_reference = -1;
+ int opt_force = 0;
+ int opt_iter = 10000000;
+ sc_context_param_t ctx_param;
+
+ setbuf(stderr, NULL);
+ setbuf(stdout, NULL);
+
+ while (1) {
+ c = getopt_long(argc, argv, "XC:I:W:U:s:i:fr:wv", options, &long_optind);
+ if (c == -1)
+ break;
+ if (c == '?')
+ util_print_usage_and_die(app_name, options, option_help, NULL);
+ switch (c) {
+ case 'X':
+ do_initialize = 1;
+ action_count++;
+ break;
+ case 'C':
+ do_create_dkek_share = 1;
+ opt_filename = optarg;
+ action_count++;
+ break;
+ case 'I':
+ do_import_dkek_share = 1;
+ opt_filename = optarg;
+ action_count++;
+ break;
+ case 'W':
+ do_wrap_key = 1;
+ opt_filename = optarg;
+ action_count++;
+ break;
+ case 'U':
+ do_unwrap_key = 1;
+ opt_filename = optarg;
+ action_count++;
+ break;
+ case OPT_PASSWORD:
+ opt_password = optarg;
+ break;
+ case OPT_SO_PIN:
+ opt_so_pin = optarg;
+ break;
+ case OPT_PIN:
+ opt_pin = optarg;
+ break;
+ case OPT_RETRY:
+ opt_retry_counter = atol(optarg);
+ break;
+ case 's':
+ opt_dkek_shares = atol(optarg);
+ break;
+ case 'f':
+ opt_force = 1;
+ break;
+ case 'i':
+ opt_key_reference = atol(optarg);
+ break;
+ case 'r':
+ opt_reader = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'w':
+ opt_wait = 1;
+ break;
+ }
+ }
+
+ CRYPTO_malloc_init();
+ ERR_load_crypto_strings();
+ OpenSSL_add_all_algorithms();
+
+ memset(&ctx_param, 0, sizeof(sc_context_param_t));
+ ctx_param.app_name = app_name;
+
+ r = sc_context_create(&ctx, &ctx_param);
+ if (r != SC_SUCCESS) {
+ fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
+ return 1;
+ }
+
+ /* Only change if not in opensc.conf */
+ if (verbose > 1 && ctx->debug == 0) {
+ ctx->debug = verbose;
+ sc_ctx_log_to_file(ctx, "stderr");
+ }
+
+ err = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose);
+ if (err)
+ goto end;
+
+ sc_path_set(&path, SC_PATH_TYPE_DF_NAME, sc_hsm_aid.value, sc_hsm_aid.len, 0, 0);
+ r = sc_select_file(card, &path, &file);
+
+ if (do_initialize) {
+ initialize(card, opt_so_pin, opt_pin, opt_retry_counter, opt_dkek_shares);
+ }
+
+ if (do_create_dkek_share) {
+ create_dkek_share(card, opt_filename, opt_iter, opt_password);
+ }
+
+ if (do_import_dkek_share) {
+ import_dkek_share(card, opt_filename, opt_iter, opt_password);
+ }
+
+ if (do_wrap_key) {
+ wrap_key(card, opt_key_reference, opt_filename, opt_pin);
+ }
+
+ if (do_unwrap_key) {
+ unwrap_key(card, opt_key_reference, opt_filename, opt_pin, opt_force);
+ }
+
+ if (action_count == 0) {
+ print_info(card, file);
+ }
+
+end:
+ if (card) {
+ sc_unlock(card);
+ sc_disconnect_card(card);
+ }
+ if (ctx)
+ sc_release_context(ctx);
+
+ ERR_print_errors_fp(stderr);
+ return err;
+}
diff --git a/win32/OpenSC.wxs.in b/win32/OpenSC.wxs.in
index d4cbcde0..d24fa710 100755
--- a/win32/OpenSC.wxs.in
+++ b/win32/OpenSC.wxs.in
@@ -103,6 +103,9 @@
+
+
+
@@ -211,6 +214,7 @@
+