diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index cc3e9aba..f9fa033d 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -10,7 +10,7 @@ noinst_HEADERS = cards.h ctbcs.h internal.h esteid.h muscle.h muscle-filesystem. opensc.h pkcs15.h \ cardctl.h asn1.h log.h \ errors.h types.h compression.h itacns.h iso7816.h \ - authentic.h iasecc.h iasecc-sdo.h sm.h + authentic.h iasecc.h iasecc-sdo.h sm.h card-sc-hsm.h AM_CPPFLAGS = -DOPENSC_CONF_PATH=\"$(sysconfdir)/opensc.conf\" AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_OPENCT_CFLAGS) \ @@ -38,13 +38,13 @@ libopensc_la_SOURCES = \ card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \ card-rtecp.c card-westcos.c card-myeid.c card-ias.c \ card-javacard.c card-itacns.c card-authentic.c \ - card-iasecc.c iasecc-sdo.c iasecc-sm.c \ + card-iasecc.c iasecc-sdo.c iasecc-sm.c card-sc-hsm.c \ \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \ pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \ pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c pkcs15-oberthur.c \ - pkcs15-itacns.c pkcs15-gemsafeV1.c \ + pkcs15-itacns.c pkcs15-gemsafeV1.c pkcs15-sc-hsm.c \ compression.c p15card-helper.c \ libopensc.exports if WIN32 diff --git a/src/libopensc/card-sc-hsm.c b/src/libopensc/card-sc-hsm.c new file mode 100644 index 00000000..59392b07 --- /dev/null +++ b/src/libopensc/card-sc-hsm.c @@ -0,0 +1,367 @@ +/* + * card-sc-hsm.c + * + * Read-only driver for the SmartCard-HSM light-weight hardware security module + * + * Copyright (C) 2012 Andreas Schwier, CardContact, Minden, Germany, and others + * + * 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 + +#include "internal.h" +#include "asn1.h" +#include "cardctl.h" +#include "types.h" + +#include "card-sc-hsm.h" + + +/* Static reference to ISO driver */ +static const struct sc_card_operations *iso_ops = NULL; + +/* Our operations */ +static struct sc_card_operations sc_hsm_ops; + +/* Our driver description */ +static struct sc_card_driver sc_hsm_drv = { + "SmartCard-HSM", + "sc-hsm", + &sc_hsm_ops, + NULL, + 0, + NULL +}; + +/* Known ATRs for SmartCard-HSMs */ +static struct sc_atr_table sc_hsm_atrs[] = { + /* standard version */ + {"3B:FE:18:00:00:81:31:FE:45:80:31:81:54:48:53:4D:31:73:80:21:40:81:07:FA", NULL, NULL, SC_CARD_TYPE_SC_HSM, 0, NULL}, + {"3B:8E:80:01:80:31:81:54:48:53:4D:31:73:80:21:40:81:07:18", NULL, NULL, SC_CARD_TYPE_SC_HSM, 0, NULL}, + {NULL, NULL, NULL, 0, 0, NULL} +}; + + +/* Information the driver maintains between calls */ +typedef struct sc_hsm_private_data { + sc_security_env_t *env; + u8 algorithm; +} sc_hsm_private_data_t; + + + +static int sc_hsm_match_card(struct sc_card *card) +{ + int i; + + i = _sc_match_atr(card, sc_hsm_atrs, &card->type); + if (i < 0) + return 0; + + return 1; +} + + + +static int sc_hsm_read_binary(sc_card_t *card, + unsigned int idx, u8 *buf, size_t count, + unsigned long flags) +{ + sc_context_t *ctx = card->ctx; + sc_apdu_t apdu; + u8 recvbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 cmdbuff[4]; + int r; + + if (idx > 0xffff) { + sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "invalid EF offset: 0x%X > 0xFFFF", idx); + return SC_ERROR_OFFSET_TOO_LARGE; + } + + cmdbuff[0] = 0x54; + cmdbuff[1] = 0x02; + cmdbuff[2] = (idx >> 8) & 0xFF; + cmdbuff[3] = idx & 0xFF; + + assert(count <= (card->max_recv_size > 0 ? card->max_recv_size : 256)); + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xB1, 0x00, 0x00); + apdu.data = cmdbuff; + apdu.datalen = 4; + apdu.lc = 4; + apdu.le = count; + apdu.resplen = count; + apdu.resp = recvbuf; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + if (apdu.resplen == 0) + SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); + memcpy(buf, recvbuf, apdu.resplen); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r == SC_ERROR_FILE_END_REACHED) + SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); + SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, r, "Check SW error"); + + if (apdu.resplen < count) { + r = sc_hsm_read_binary(card, idx + apdu.resplen, buf + apdu.resplen, count - apdu.resplen, flags); + /* Ignore all but 'corrupted data' errors */ + if (r == SC_ERROR_CORRUPTED_DATA) + SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_CORRUPTED_DATA); + else if (r > 0) + apdu.resplen += r; + } + + SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); +} + + + +static int sc_hsm_list_files(sc_card_t *card, u8 * buf, size_t buflen) +{ + sc_apdu_t apdu; + u8 recvbuf[MAX_EXT_APDU_LENGTH]; + int r; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_EXT, 0x58, 0, 0); + apdu.cla = 0x80; + apdu.resp = recvbuf; + apdu.resplen = sizeof(recvbuf); + apdu.le = 0; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "ENUMERATE OBJECTS APDU transmit failed"); + + memcpy(buf, recvbuf, buflen); + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); +} + + + +static int sc_hsm_set_security_env(sc_card_t *card, + const sc_security_env_t *env, + int se_num) +{ + sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; + + priv->env = env; + + switch(env->algorithm) { + case SC_ALGORITHM_RSA: +// if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { +// if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) { +// priv->algorithm = ALGO_RSA_PKCS1_SHA1; +// } else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA256) { +// priv->algorithm = ALGO_RSA_PKCS1_SHA256; +// } else { +// SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); +// } +// } else { + priv->algorithm = ALGO_RSA_RAW; +// } + break; + case SC_ALGORITHM_EC: + if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_NONE) { + priv->algorithm = ALGO_EC_RAW; + } else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA1) { + priv->algorithm = ALGO_EC_SHA1; + } else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA224) { + priv->algorithm = ALGO_EC_SHA224; + } else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_HASH_SHA256) { + priv->algorithm = ALGO_EC_SHA256; + } else if (env->algorithm_flags & SC_ALGORITHM_ECDSA_RAW) { + priv->algorithm = ALGO_EC_RAW; + } else { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + } + break; + default: + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); + } + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS); +} + + + +static int sc_hsm_compute_signature(sc_card_t *card, + const u8 * data, size_t datalen, + u8 * out, size_t outlen) +{ + int r; + sc_apdu_t apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; + + assert(card != NULL && data != NULL && out != NULL); + + if (priv->env == NULL) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_OBJECT_NOT_FOUND); + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x68, priv->env->key_ref[0], priv->algorithm); + apdu.cla = 0x80; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); /* FIXME */ + apdu.le = 256; + + memcpy(sbuf, data, datalen); + apdu.data = sbuf; + apdu.lc = datalen; + apdu.datalen = datalen; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + size_t len = apdu.resplen > outlen ? outlen : apdu.resplen; + + memcpy(out, apdu.resp, len); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len); + } + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} + + + +static int sc_hsm_decipher(sc_card_t *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) +{ + int r; + sc_apdu_t apdu; + sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; + + assert(card != NULL && crgram != NULL && out != NULL); + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x62, priv->env->key_ref[0], 0x21); + apdu.cla = 0x80; + apdu.resp = out; + apdu.resplen = outlen; + /* if less than 256 bytes are expected than set Le to 0x00 + * to tell the card the we want everything available (note: we + * always have Le <= crgram_len) */ + apdu.le = (outlen >= 256 && crgram_len < 256) ? 256 : outlen; + + apdu.data = crgram; + apdu.lc = crgram_len; + apdu.datalen = crgram_len; + + r = sc_transmit_apdu(card, &apdu); + + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen); + else + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} + + + +static int sc_hsm_init(struct sc_card *card) +{ + sc_hsm_private_data_t *priv; + int flags,ext_flags; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + priv = calloc(1, sizeof(sc_hsm_private_data_t)); + if (!priv) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + + card->drv_data = priv; + + flags = SC_ALGORITHM_RSA_RAW; +// SC_ALGORITHM_RSA_PAD_PKCS1| +// SC_ALGORITHM_RSA_HASH_SHA1| +// SC_ALGORITHM_RSA_HASH_SHA256; + + _sc_card_add_rsa_alg(card, 1024, flags, 0); + _sc_card_add_rsa_alg(card, 1536, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); + +#if 0 + flags = SC_ALGORITHM_ECDSA_RAW| + SC_ALGORITHM_ECDSA_HASH_NONE| + SC_ALGORITHM_ECDSA_HASH_SHA1| + SC_ALGORITHM_ECDSA_HASH_SHA224| + SC_ALGORITHM_ECDSA_HASH_SHA256; +#endif + + flags = SC_ALGORITHM_ECDSA_HASH_NONE| + SC_ALGORITHM_ECDSA_HASH_SHA1| + SC_ALGORITHM_ECDSA_HASH_SHA224| + SC_ALGORITHM_ECDSA_HASH_SHA256; + + ext_flags = SC_ALGORITHM_EXT_EC_F_P| + SC_ALGORITHM_EXT_EC_ECPARAMETERS| + SC_ALGORITHM_EXT_EC_UNCOMPRESES; + _sc_card_add_ec_alg(card, 192, flags, ext_flags); + _sc_card_add_ec_alg(card, 224, flags, ext_flags); + _sc_card_add_ec_alg(card, 256, flags, ext_flags); + _sc_card_add_ec_alg(card, 320, flags, ext_flags); + + card->caps |= SC_CARD_CAP_RNG|SC_CARD_CAP_APDU_EXT; + return 0; +} + + + +static int sc_hsm_finish(sc_card_t * card) +{ + sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; + free(priv); + return SC_SUCCESS; +} + + + +static struct sc_card_driver * sc_get_driver(void) +{ + struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); + + if (iso_ops == NULL) + iso_ops = iso_drv->ops; + + sc_hsm_ops = *iso_drv->ops; + sc_hsm_ops.match_card = sc_hsm_match_card; + sc_hsm_ops.read_binary = sc_hsm_read_binary; + sc_hsm_ops.list_files = sc_hsm_list_files; + sc_hsm_ops.set_security_env = sc_hsm_set_security_env; + sc_hsm_ops.compute_signature = sc_hsm_compute_signature; + sc_hsm_ops.decipher = sc_hsm_decipher; + sc_hsm_ops.init = sc_hsm_init; + sc_hsm_ops.finish = sc_hsm_finish; + + /* no record oriented file services */ + sc_hsm_ops.read_record = NULL; + sc_hsm_ops.write_record = NULL; + sc_hsm_ops.append_record = NULL; + sc_hsm_ops.update_record = NULL; + sc_hsm_ops.update_binary = NULL; + sc_hsm_ops.create_file = NULL; + sc_hsm_ops.delete_file = NULL; + + return &sc_hsm_drv; +} + + + +struct sc_card_driver * sc_get_sc_hsm_driver(void) +{ + return sc_get_driver(); +} + diff --git a/src/libopensc/card-sc-hsm.h b/src/libopensc/card-sc-hsm.h new file mode 100644 index 00000000..b5596921 --- /dev/null +++ b/src/libopensc/card-sc-hsm.h @@ -0,0 +1,42 @@ +/* + * sc-hsm.h + * + * Copyright (C) 2012 Andreas Schwier, CardContact, 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 + */ + +#ifndef SC_HSM_H_ +#define SC_HSM_H_ + +#define MAX_EXT_APDU_LENGTH 1014 + +#define KEY_PREFIX 0xCC /* Hi byte in file identifier for key objects */ +#define PRKD_PREFIX 0xC4 /* Hi byte in file identifier for PRKD objects */ +#define EE_CERTIFICATE_PREFIX 0xCE /* Hi byte in file identifier for EE certificates */ + +#define ALGO_RSA_RAW 0x20 /* RSA signature with external padding */ +#define ALGO_RSA_PKCS1_SHA1 0x31 /* RSA signature with SHA-1 hash and PKCS#1 V1.5 padding */ +#define ALGO_RSA_PKCS1_SHA256 0x33 /* RSA signature with SHA-256 hash and PKCS#1 V1.5 padding */ + +#define ALGO_RSA_PSS_SHA1 0x41 /* RSA signature with SHA-1 hash and PKCS#1 PSS padding */ +#define ALGO_RSA_PSS_SHA256 0x43 /* RSA signature with SHA-256 hash and PKCS#1 PSS padding */ + +#define ALGO_EC_RAW 0x70 /* ECDSA signature with hash input */ +#define ALGO_EC_SHA1 0x71 /* ECDSA signature with SHA-1 hash */ +#define ALGO_EC_SHA224 0x72 /* ECDSA signature with SHA-224 hash */ +#define ALGO_EC_SHA256 0x73 /* ECDSA signature with SHA-256 hash */ + +#endif /* SC_HSM_H_ */ diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index 84f384b3..0fbf9ca8 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -187,7 +187,10 @@ enum { SC_CARD_TYPE_IASECC_GEMALTO, SC_CARD_TYPE_IASECC_OBERTHUR, SC_CARD_TYPE_IASECC_SAGEM, - SC_CARD_TYPE_IASECC_AMOS + SC_CARD_TYPE_IASECC_AMOS, + + /* SmartCard-HSM */ + SC_CARD_TYPE_SC_HSM = 26000, }; extern sc_card_driver_t *sc_get_default_driver(void); @@ -218,6 +221,7 @@ extern sc_card_driver_t *sc_get_rtecp_driver(void); extern sc_card_driver_t *sc_get_westcos_driver(void); extern sc_card_driver_t *sc_get_myeid_driver(void); extern sc_card_driver_t *sc_get_ias_driver(void); +extern sc_card_driver_t *sc_get_sc_hsm_driver(void); extern sc_card_driver_t *sc_get_javacard_driver(void); extern sc_card_driver_t *sc_get_itacns_driver(void); extern sc_card_driver_t *sc_get_authentic_driver(void); diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index 2e85aaa6..986e0fc0 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -96,6 +96,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = { { "rutoken_ecp",(void *(*)(void)) sc_get_rtecp_driver }, { "westcos", (void *(*)(void)) sc_get_westcos_driver }, { "myeid", (void *(*)(void)) sc_get_myeid_driver }, + { "sc-hsm", (void *(*)(void)) sc_get_sc_hsm_driver }, /* Here should be placed drivers that need some APDU transactions to * recognise its cards. */ diff --git a/src/libopensc/pkcs15-prkey.c b/src/libopensc/pkcs15-prkey.c index 4125623b..f966d703 100644 --- a/src/libopensc/pkcs15-prkey.c +++ b/src/libopensc/pkcs15-prkey.c @@ -130,14 +130,35 @@ static const struct sc_asn1_entry c_asn1_prk_dsa_attr[C_ASN1_PRK_DSA_ATTR_SIZE] { NULL, 0, 0, 0, NULL, NULL } }; -#define C_ASN1_PRKEY_SIZE 4 +/* + * The element fieldSize is a proprietary extension to ISO 7816-15, providing to the middleware + * the size of the underlying ECC field. This value is required for determine a proper size for + * buffer allocations. The field follows the definition for modulusLength in RSA keys + */ +#define C_ASN1_ECCKEY_ATTR 4 +static const struct sc_asn1_entry c_asn1_ecckey_attr[C_ASN1_ECCKEY_ATTR] = { + { "value", SC_ASN1_PATH, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_EMPTY_ALLOWED, NULL, NULL }, + { "fieldSize", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { "keyInfo", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, SC_ASN1_OPTIONAL, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; + +#define C_ASN1_PRK_ECC_ATTR 2 +static const struct sc_asn1_entry c_asn1_prk_ecc_attr[C_ASN1_PRK_ECC_ATTR] = { + { "privateECCKeyAttributes", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } +}; + +#define C_ASN1_PRKEY_SIZE 5 static const struct sc_asn1_entry c_asn1_prkey[C_ASN1_PRKEY_SIZE] = { { "privateRSAKey", SC_ASN1_PKCS15_OBJECT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, + { "privateECCKey", SC_ASN1_PKCS15_OBJECT, 0 | SC_ASN1_CTX | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "privateDSAKey", SC_ASN1_PKCS15_OBJECT, 2 | SC_ASN1_CTX | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { "privateGOSTR3410Key", SC_ASN1_PKCS15_OBJECT, 4 | SC_ASN1_CTX | SC_ASN1_CONS, SC_ASN1_OPTIONAL, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL } }; + int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *obj, const u8 ** buf, size_t *buflen) @@ -158,11 +179,13 @@ int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, struct sc_asn1_entry asn1_dsakey_value_attr[C_ASN1_DSAKEY_VALUE_ATTR_SIZE]; struct sc_asn1_entry asn1_gostr3410key_attr[C_ASN1_GOSTR3410KEY_ATTR_SIZE]; struct sc_asn1_entry asn1_prk_gostr3410_attr[C_ASN1_PRK_GOSTR3410_ATTR_SIZE]; + struct sc_asn1_entry asn1_ecckey_attr[C_ASN1_ECCKEY_ATTR], asn1_prk_ecc_attr[C_ASN1_PRK_ECC_ATTR]; struct sc_asn1_entry asn1_prkey[C_ASN1_PRKEY_SIZE]; struct sc_asn1_entry asn1_supported_algorithms[C_ASN1_SUPPORTED_ALGORITHMS_SIZE]; struct sc_asn1_pkcs15_object rsa_prkey_obj = {obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_rsa_attr}; struct sc_asn1_pkcs15_object dsa_prkey_obj = {obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_dsa_attr}; struct sc_asn1_pkcs15_object gostr3410_prkey_obj = {obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_gostr3410_attr}; + struct sc_asn1_pkcs15_object ecc_prkey_obj = { obj, asn1_com_key_attr, asn1_com_prkey_attr, asn1_prk_ecc_attr }; sc_copy_asn1_entry(c_asn1_prkey, asn1_prkey); sc_copy_asn1_entry(c_asn1_supported_algorithms, asn1_supported_algorithms); @@ -175,6 +198,8 @@ int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, sc_copy_asn1_entry(c_asn1_dsakey_i_p_attr, asn1_dsakey_i_p_attr); sc_copy_asn1_entry(c_asn1_prk_gostr3410_attr, asn1_prk_gostr3410_attr); sc_copy_asn1_entry(c_asn1_gostr3410key_attr, asn1_gostr3410key_attr); + sc_copy_asn1_entry(c_asn1_prk_ecc_attr, asn1_prk_ecc_attr); + sc_copy_asn1_entry(c_asn1_ecckey_attr, asn1_ecckey_attr); sc_copy_asn1_entry(c_asn1_com_prkey_attr, asn1_com_prkey_attr); sc_copy_asn1_entry(c_asn1_com_key_attr, asn1_com_key_attr); @@ -182,10 +207,12 @@ int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, sc_format_asn1_entry(asn1_prkey + 0, &rsa_prkey_obj, NULL, 0); sc_format_asn1_entry(asn1_prkey + 1, &dsa_prkey_obj, NULL, 0); sc_format_asn1_entry(asn1_prkey + 2, &gostr3410_prkey_obj, NULL, 0); + sc_format_asn1_entry(asn1_prkey + 3, &ecc_prkey_obj, NULL, 0); sc_format_asn1_entry(asn1_prk_rsa_attr + 0, asn1_rsakey_attr, NULL, 0); sc_format_asn1_entry(asn1_prk_dsa_attr + 0, asn1_dsakey_attr, NULL, 0); sc_format_asn1_entry(asn1_prk_gostr3410_attr + 0, asn1_gostr3410key_attr, NULL, 0); + sc_format_asn1_entry(asn1_prk_ecc_attr + 0, asn1_ecckey_attr, NULL, 0); sc_format_asn1_entry(asn1_rsakey_attr + 0, &info.path, NULL, 0); sc_format_asn1_entry(asn1_rsakey_attr + 1, &info.modulus_length, NULL, 0); @@ -200,6 +227,9 @@ int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, sc_format_asn1_entry(asn1_gostr3410key_attr + 2, &gostr3410_params[1], NULL, 0); sc_format_asn1_entry(asn1_gostr3410key_attr + 3, &gostr3410_params[2], NULL, 0); + sc_format_asn1_entry(asn1_ecckey_attr + 0, &info.path, NULL, 0); + sc_format_asn1_entry(asn1_ecckey_attr + 1, &info.field_length, NULL, 0); + sc_format_asn1_entry(asn1_com_key_attr + 0, &info.id, NULL, 0); sc_format_asn1_entry(asn1_com_key_attr + 1, &info.usage, &usage_len, 0); sc_format_asn1_entry(asn1_com_key_attr + 2, &info.native, NULL, 0); @@ -246,8 +276,11 @@ int sc_pkcs15_decode_prkdf_entry(struct sc_pkcs15_card *p15card, keyinfo_gostparams->gostr3411 = gostr3410_params[1]; keyinfo_gostparams->gost28147 = gostr3410_params[2]; } + else if (asn1_prkey[3].flags & SC_ASN1_PRESENT) { + obj->type = SC_PKCS15_TYPE_PRKEY_EC; + } else { - sc_log(ctx, "Neither RSA or DSA or GOSTR3410 key in PrKDF entry."); + sc_log(ctx, "Neither RSA or DSA or GOSTR3410 or ECC key in PrKDF entry."); LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ASN1_OBJECT); } diff --git a/src/libopensc/pkcs15-sc-hsm.c b/src/libopensc/pkcs15-sc-hsm.c new file mode 100644 index 00000000..8738e5b7 --- /dev/null +++ b/src/libopensc/pkcs15-sc-hsm.c @@ -0,0 +1,205 @@ +/* + * pkcs15-sc-hsm.c : Initialize PKCS#15 emulation + * + * Copyright (C) 2012 Andreas Schwier, CardContact, 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 +#include + +#include "internal.h" +#include "pkcs15.h" +#include "asn1.h" + +#include "card-sc-hsm.h" + + +/* Our AID */ +static struct sc_aid sc_hsm_aid = { { 0xE8,0x2B,0x06,0x01,0x04,0x01,0x81,0xC3,0x1F,0x02,0x01 }, 11 }; + +/* + * Initialize PKCS#15 emulation with user PIN, private keys and certificate objects + * + */ +static int sc_pkcs15emu_sc_hsm_init (sc_pkcs15_card_t * p15card) +{ + sc_card_t *card = p15card->card; + sc_file_t *file = NULL; + sc_path_t path; + u8 filelist[MAX_EXT_APDU_LENGTH]; + int filelistlength; + int r, i; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + p15card->tokeninfo->label = strdup("SmartCard-HSM"); + p15card->tokeninfo->manufacturer_id = strdup("CardContact"); + + struct sc_app_info *appinfo = calloc(1, sizeof(struct sc_app_info)); + + if (appinfo == NULL) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + } + + appinfo->label = strdup(p15card->tokeninfo->label); + appinfo->aid = sc_hsm_aid; + + appinfo->ddo.aid = sc_hsm_aid; + p15card->app = appinfo; + + 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); + + // ToDo: Extract version number + sc_file_free(file); + + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Could not select SmartCard-HSM application"); + + // Define UserPIN + struct sc_pkcs15_auth_info pin_info; + struct sc_pkcs15_object pin_obj; + + memset(&pin_info, 0, sizeof(pin_info)); + memset(&pin_obj, 0, sizeof(pin_obj)); + + pin_info.auth_id.len = 1; + pin_info.auth_id.value[0] = 1; + pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; + pin_info.attrs.pin.reference = 0x81; + pin_info.attrs.pin.flags = 0; + pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; + pin_info.attrs.pin.min_length = 4; + pin_info.attrs.pin.stored_length = 0; + pin_info.attrs.pin.max_length = 16; + pin_info.attrs.pin.pad_char = '\0'; + pin_info.tries_left = 3; + pin_info.max_tries = 3; + + strlcpy(pin_obj.label, "UserPIN", sizeof(pin_obj.label)); + pin_obj.flags = pin_info.attrs.pin.flags; + + r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); + if (r < 0) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + + filelistlength = sc_list_files(card, filelist, sizeof(filelist)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Could not enumerate file and key identifier"); + + for (i = 0; i < filelistlength; i += 2) { + /* Look for private key files */ + if (filelist[i] != KEY_PREFIX) { + continue; + } + + u8 fid[2]; + u8 prkdbin[512]; + sc_pkcs15_object_t prkd; + u8 keyid = filelist[i + 1]; + + 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, &file); + + if (r != SC_SUCCESS) { + continue; + } + + sc_file_free(file); + r = sc_read_binary(p15card->card, 0, prkdbin, sizeof(prkdbin), 0); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Could not read EF.PRKD"); + + memset(&prkd, 0, sizeof(prkd)); + const u8 *ptr = prkdbin; + size_t len = r; + + sc_pkcs15_decode_prkdf_entry(p15card, &prkd, &ptr, &len); + + /* All keys require user PIN authentication */ + prkd.auth_id.len = 1; + prkd.auth_id.value[0] = 1; + + /* + * Set private key flag as all keys are private anyway + */ + prkd.flags |= SC_PKCS15_CO_FLAG_PRIVATE; + + sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *)prkd.data; + key_info->key_reference = keyid; + + /* + * Set path.aid.len to 0 to prevent re-selection of applet when using the key + */ + key_info->path.aid.len = 0; + + if (prkd.type == SC_PKCS15_TYPE_PRKEY_RSA) { + r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkd, key_info); + } else { + r = sc_pkcs15emu_add_ec_prkey(p15card, &prkd, key_info); + } + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Could not decode EF.PRKD"); + + /* Check if we also have a certificate for the private key */ + fid[0] = EE_CERTIFICATE_PREFIX; + + sc_path_set(&path, SC_PATH_TYPE_FILE_ID, fid, sizeof(fid), 0, 0); + r = sc_select_file(card, &path, &file); + + if (r != SC_SUCCESS) { + continue; + } + + sc_file_free(file); + + struct sc_pkcs15_cert_info cert_info; + struct sc_pkcs15_object cert_obj; + + memset(&cert_info, 0, sizeof(cert_info)); + memset(&cert_obj, 0, sizeof(cert_obj)); + + cert_info.id.value[0] = keyid; + cert_info.id.len = 1; + cert_info.path = path; + cert_info.path.count = -1; + + strlcpy(cert_obj.label, prkd.label, sizeof(cert_obj.label)); + r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Could not add certificate"); + } + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); +} + + + +int sc_pkcs15emu_sc_hsm_init_ex(sc_pkcs15_card_t *p15card, + sc_pkcs15emu_opt_t *opts) +{ + if (opts && (opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)) { + return sc_pkcs15emu_sc_hsm_init(p15card); + } else { + if (p15card->card->type != SC_CARD_TYPE_SC_HSM) { + return SC_ERROR_WRONG_CARD; + } + return sc_pkcs15emu_sc_hsm_init(p15card); + } +} diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c index 43e1218a..e2f60043 100644 --- a/src/libopensc/pkcs15-syn.c +++ b/src/libopensc/pkcs15-syn.c @@ -65,6 +65,8 @@ extern int sc_pkcs15emu_oberthur_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); extern int sc_pkcs15emu_itacns_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); +extern int sc_pkcs15emu_sc_hsm_init_ex(sc_pkcs15_card_t *, + sc_pkcs15emu_opt_t *); static struct { const char * name; @@ -87,6 +89,7 @@ static struct { { "entersafe", sc_pkcs15emu_entersafe_init_ex }, { "pteid", sc_pkcs15emu_pteid_init_ex }, { "oberthur", sc_pkcs15emu_oberthur_init_ex }, + { "sc-hsm", sc_pkcs15emu_sc_hsm_init_ex }, { NULL, NULL } }; @@ -109,6 +112,7 @@ int sc_pkcs15_is_emulation_only(sc_card_t *card) case SC_CARD_TYPE_GEMSAFEV1_PTEID: case SC_CARD_TYPE_OPENPGP_V1: case SC_CARD_TYPE_OPENPGP_V2: + case SC_CARD_TYPE_SC_HSM: return 1; default: return 0;