diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 000ca09a8b..c4df11376e 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -11,7 +11,7 @@ noinst_HEADERS = cards.h ctbcs.h internal.h esteid.h muscle.h muscle-filesystem. 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 card-sc-hsm.h \ - pace.h cwa14890.h cwa-dnie.h + pace.h cwa14890.h cwa-dnie.h card-gids.h AM_CPPFLAGS = -DOPENSC_CONF_PATH=\"$(sysconfdir)/opensc.conf\" \ -I$(top_srcdir)/src @@ -41,14 +41,14 @@ libopensc_la_SOURCES = \ card-itacns.c card-authentic.c \ card-iasecc.c iasecc-sdo.c iasecc-sm.c card-sc-hsm.c \ card-dnie.c cwa14890.c cwa-dnie.c \ - card-isoApplet.c card-masktech.c \ + card-isoApplet.c card-masktech.c card-gids.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-sc-hsm.c \ - pkcs15-dnie.c \ + pkcs15-dnie.c pkcs15-gids.c \ compression.c p15card-helper.c sm.c \ libopensc.exports if WIN32 diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak index 1c078f4b0e..c18dc1f08a 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -24,14 +24,14 @@ OBJECTS = \ card-itacns.obj card-authentic.obj \ card-iasecc.obj iasecc-sdo.obj iasecc-sm.obj cwa-dnie.obj cwa14890.obj \ card-sc-hsm.obj card-dnie.obj card-isoApplet.obj \ - card-masktech.obj \ + card-masktech.obj card-gids.obj \ \ pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \ pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \ pkcs15-actalis.obj pkcs15-atrust-acos.obj pkcs15-tccardos.obj pkcs15-piv.obj \ pkcs15-esinit.obj pkcs15-westcos.obj pkcs15-pteid.obj pkcs15-oberthur.obj \ pkcs15-itacns.obj pkcs15-gemsafeV1.obj pkcs15-sc-hsm.obj \ - pkcs15-dnie.obj \ + pkcs15-dnie.obj pkcs15-gids.obj \ compression.obj p15card-helper.obj sm.obj \ $(TOPDIR)\win32\versioninfo.res diff --git a/src/libopensc/card-gids.c b/src/libopensc/card-gids.c new file mode 100644 index 0000000000..1996eb1cfa --- /dev/null +++ b/src/libopensc/card-gids.c @@ -0,0 +1,2035 @@ +/* + * card-gids.c: Support for GIDS smart cards. + * + * Copyright (C) 2015 Vincent Le Toux (My Smart Logon) + * + * 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 + */ + +/* +The GIDS specification can be viewed here: +https://msdn.microsoft.com/en-us/library/windows/hardware/dn642100%28v=vs.85%29.aspx + +and its formatting into the MS minidriver specification. +Some features are undocumented like the format used to store certificates. They have been reverse engineered. +*/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#ifdef ENABLE_OPENSSL +/* openssl only needed for card administration */ +#include +#include +#endif /* ENABLE_OPENSSL */ + +#include "internal.h" +#include "asn1.h" +#include "cardctl.h" +#include "iso7816.h" + +#ifdef ENABLE_ZLIB + +#include "compression.h" +// used for changing the default label if used twice +#include "../pkcs15init/pkcs15-init.h" +#include "card-gids.h" + +#define GIDS_STATE_NONE 0 +#define GIDS_STATE_READ_DATA_PRESENT 1 + +#define INS_ACTIVATE_FILE 0x44 +#define INS_CREATE_FILE 0xE0 +#define INS_DELETE_FILE 0xE4 +#define INS_GENERAL_AUTHENTICATE 0x87 +#define INS_GENERATE_ASYMECTRIC_KEY_PAIR 0x47 +#define INS_GET_DATA 0xCB +#define INS_MANAGE_SECURITY_ENVIRONMENT 0x22 +#define INS_PUT_DATA 0xDB +#define INS_SELECT 0xA4 +#define INS_VERIFY 0x20 + +#define P1_SELECT_DF_OR_EF_WITH_EFID 0x00 +#define P1_SELECT_DF_BY_NAME 0x04 +#define P1_DECIPHERMENT_INTERNAL_AUTHENTICATE_KEY_AGREEMENT 0x41 + +#define P2_SELECT_FIRST_OR_ONLY_OCCURENCE 0x00 +#define P2_PIN_DEAUTHENTICATE 0x82 +#define P2_DIGITAL_SIGNATURE 0xB6 +#define P2_DECIPHERMENT 0xB8 + +#define GIDS_PIN_STATUS_OBJECT_IDENTIFIER 0x7F71 +#define GIDS_PUK_STATUS_OBJECT_IDENTIFIER 0x7F73 +#define GIDS_APPLET_EFID 0x3FFF +#define GIDS_PUT_KEY_DO 0x70 +#define GIDS_RSA_1024_IDENTIFIER 0x06 +#define GIDS_RSA_2048_IDENTIFIER 0x07 +#define GIDS_RSA_3072_IDENTIFIER 0x08 +#define GIDS_RSA_4096_IDENTIFIER 0x09 +#define GIDS_ECC_192_IDENTIFIER 0x0A +#define GIDS_ECC_224_IDENTIFIER 0x0B +#define GIDS_ECC_256_IDENTIFIER 0x0C +#define GIDS_ECC_384_IDENTIFIER 0x0D +#define GIDS_ECC_521_IDENTIFIER 0x0E + +#define GIDS_PUBKEY_TAG 0x7F49 +#define GIDS_PUBKEY_TAG_MODULUS 0x81 +#define GIDS_PUBKEY_TAG_EXPONENT 0x82 + +#define GIDS_FIRST_KEY_IDENTIFIER 0x81 + +#define GIDS_PIN_IDENTIFIER 0x80 +#define GIDS_PUK_IDENTIFIER 0x81 +#define GIDS_TRY_COUNTER_OLD_TAG 0x9F17 +#define GIDS_TRY_COUNTER_TAG 0x97 +#define GIDS_TRY_LIMIT_TAG 0x93 +#define GIDS_APPLICATION_TEMPLATE_TAG 0x61 +#define GIDS_APPLICATION_AID_TAG 0x4F + +#define GIDS_KEY_TYPE_AT_KEYEXCHANGE 0x9A +#define GIDS_KEY_TYPE_AT_SIGNATURE 0x9C + +static struct sc_card_operations *iso_ops; +static struct sc_card_operations gids_ops; +static struct sc_card_driver gids_drv = { + "GIDS Smart Card", + "gids", + &gids_ops, + NULL, 0, NULL +}; + +struct gids_aid { + int enumtag; + size_t len_short; /* min lenght without version */ + size_t len_long; /* With version and other stuff */ + u8 *value; +}; + +/* GIDS AID */ +struct sc_aid gids_aid = { { 0xA0,0x00,0x00,0x03,0x97,0x42,0x54,0x46,0x59 }, 9 }; + +static struct gids_aid gids_aids[] = { + {SC_CARD_TYPE_GIDS_V1, + 9, 10, (u8 *) "\xA0\x00\x00\x03\x97\x42\x54\x46\x59\x01" }, + {SC_CARD_TYPE_GIDS_V2, + 9, 10, (u8 *) "\xA0\x00\x00\x03\x97\x42\x54\x46\x59\x02" }, + {0, 9, 0, NULL } +}; + +// stolen from cardmod.h for the cardcf file +typedef struct _CARD_CACHE_FILE_FORMAT +{ + unsigned char bVersion; // Cache version + unsigned char bPinsFreshness; // Card PIN + unsigned short wContainersFreshness; + unsigned short wFilesFreshness; + +} CARD_CACHE_FILE_FORMAT, *PCARD_CACHE_FILE_FORMAT; + +struct gids_private_data { + u8 masterfile[MAX_GIDS_FILE_SIZE]; + size_t masterfilesize; + u8 cmapfile[MAX_GIDS_FILE_SIZE]; + size_t cmapfilesize; + unsigned short currentEFID; + unsigned short currentDO; + int state; + u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE]; + size_t buffersize; +}; + +// LOW LEVEL API +/////////////////////////////////////////// + +// find file identifier & DO identifier from the masterfile for a directory/file +static int gids_get_identifiers(sc_card_t* card, u8* masterfile, size_t masterfilesize, char *directory, char *filename, int *fileIdentifier, int *dataObjectIdentifier) { + gids_mf_record_t *records = (gids_mf_record_t *) (masterfile+1); + size_t recordcount = ((masterfilesize-1) / sizeof(gids_mf_record_t)); + size_t i; + assert(masterfilesize >= 1); + + for (i = 0; i < recordcount; i++) { + if (strcmp(directory, records[i].directory) == 0 && strcmp(filename, records[i].filename) == 0) { + *fileIdentifier = records[i].fileIdentifier; + *dataObjectIdentifier = records[i].dataObjectIdentifier; + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "Identifiers of %s %s is fileIdentifier=%x, dataObjectIdentifier=%x\n", directory, filename, *fileIdentifier, *dataObjectIdentifier); + return 0; + } + } + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "file %s %s not found\n", directory, filename); + return SC_ERROR_FILE_NOT_FOUND; +} + +// used when storing a new certificates +static int gids_find_available_DO(sc_card_t *card, u8* masterfile, size_t masterfilesize, int* fileIdentifier, int *dataObjectIdentifier) { + // find the first available DO from the masterfile since A010 DF21 + // A010 = read everyone, card user write + gids_mf_record_t *records = (gids_mf_record_t *) (masterfile+1); + size_t recordcount = (masterfilesize / sizeof(gids_mf_record_t)); + size_t i; + + assert(masterfilesize >= 1); + + *fileIdentifier = CERT_FI; + + for (*dataObjectIdentifier = CARDAPPS_DO; *dataObjectIdentifier < GIDS_MAX_DO; (*dataObjectIdentifier)++) { + for (i = 0; i < recordcount; i++) { + if (records[i].fileIdentifier == *fileIdentifier && records[i].dataObjectIdentifier == *dataObjectIdentifier) { + break; + } + } + if (i == recordcount) { + return SC_SUCCESS; + } + } + return SC_ERROR_NOT_ENOUGH_MEMORY; +} + +// read a DO from the card +static int gids_get_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdentifier, u8* response, size_t *responselen) { + sc_apdu_t apdu; + int r; + u8 data[4] = {0x5C, 0x02, (dataObjectIdentifier&0xFF00)>>8, (dataObjectIdentifier&0xFF)}; + size_t datasize = 0; + const u8* p; + u8 buffer[MAX_GIDS_FILE_SIZE]; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "Got args: fileIdentifier=%x, dataObjectIdentifier=%x, response=%x, responselen=%d\n", + fileIdentifier, dataObjectIdentifier, response, responselen ? *responselen : 0); + + sc_format_apdu(card, &apdu, + response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, INS_GET_DATA, (fileIdentifier&0xFF00)>>8, (fileIdentifier&0xFF)); + apdu.lc = 04; + apdu.data = data; + apdu.datalen = 04; + apdu.resp = buffer; + apdu.resplen = sizeof(buffer); + apdu.le = 256; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids get data failed"); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + + p = sc_asn1_find_tag(card->ctx, buffer, sizeof(buffer), dataObjectIdentifier, &datasize); + if (!p) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND); + } + if (datasize > *responselen) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_BUFFER_TOO_SMALL); + } + memcpy(response, p, datasize); + *responselen = datasize; + return SC_SUCCESS; +} + +// write a DO to the card +static int gids_put_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdentifier, u8 *data, size_t datalen) { + sc_apdu_t apdu; + int r; + u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE]; + u8* p = buffer; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "Got args: fileIdentifier=%x, dataObjectIdentifier=%x, data=%x, datalen=%d\n", + fileIdentifier, dataObjectIdentifier, data, datalen); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_PUT_DATA, (fileIdentifier&0xFF00)>>8, (fileIdentifier&0xFF)); + + r = sc_asn1_put_tag(dataObjectIdentifier, data, datalen, buffer, sizeof(buffer), &p); + LOG_TEST_RET(card->ctx, r, "Error handling TLV."); + + apdu.data = buffer; + apdu.datalen = (size_t) (p - buffer); + apdu.lc = apdu.datalen; + apdu.flags |= SC_APDU_FLAGS_CHAINING; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids put data failed"); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + + return SC_SUCCESS; +} + +// select the GIDS applet +static int gids_select_aid(sc_card_t* card, u8* aid, size_t aidlen, u8* response, size_t *responselen) +{ + sc_apdu_t apdu; + int r; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "Got args: aid=%x, aidlen=%d, response=%x, responselen=%d\n", + aid, aidlen, response, responselen ? *responselen : 0); + + sc_format_apdu(card, &apdu, + response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, INS_SELECT, P1_SELECT_DF_BY_NAME, P2_SELECT_FIRST_OR_ONLY_OCCURENCE); + apdu.lc = aidlen; + apdu.data = aid; + apdu.datalen = aidlen; + apdu.resp = response; + apdu.resplen = responselen ? *responselen : 0; + apdu.le = response == NULL ? 0 : 256; /* could be 21 for fci */ + + r = sc_transmit_apdu(card, &apdu); + if (responselen) + *responselen = apdu.resplen; + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids select failed"); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} + +// DIRECT FILE MANIPULATION +/////////////////////////////////////////// + +// read a file given the masterfile +static int gids_read_gidsfile_without_cache(sc_card_t* card, u8* masterfile, size_t masterfilesize, char *directory, char *filename, u8* response, size_t *responselen) { + int r; + int fileIdentifier; + int dataObjectIdentifier; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + r = gids_get_identifiers(card, masterfile, masterfilesize, directory, filename, &fileIdentifier, &dataObjectIdentifier); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the identifier for the gids file"); + r = gids_get_DO(card, fileIdentifier, dataObjectIdentifier, response, responselen); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the data from the file"); + return r; +} + +// write a file given the masterfile +static int gids_write_gidsfile_without_cache(sc_card_t* card, u8* masterfile, size_t masterfilesize, char *directory, char *filename, u8* data, size_t datalen) { + int r; + int fileIdentifier; + int dataObjectIdentifier; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + if (datalen > MAX_GIDS_FILE_SIZE) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA); + } + + r = gids_get_identifiers(card, masterfile, masterfilesize, directory, filename, &fileIdentifier, &dataObjectIdentifier); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the identifier for the gids file"); + r = gids_put_DO(card, fileIdentifier, dataObjectIdentifier, data, datalen); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the data from the file"); + return r; +} + +// read the masterfile from the card +static int gids_read_masterfile(sc_card_t* card) { + struct gids_private_data* data = (struct gids_private_data*) card->drv_data; + int r = SC_SUCCESS; + + data->masterfilesize = sizeof(data->masterfile); + r = gids_get_DO(card, MF_FI, MF_DO, data->masterfile, &data->masterfilesize); + if (r<0) { + data->masterfilesize = sizeof(data->masterfile); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_CARD); + } + if (data->masterfilesize < 1 || data->masterfile[0] != 1) { + data->masterfilesize = sizeof(data->masterfile); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_CARD); + } + return r; +} + +// signal to the windows minidriver that something changed on the card and that it should refresh its cache +// the format of this file is specified in the minidriver specification +static int gids_update_cardcf(sc_card_t* card, int file, int container) { + struct gids_private_data* data = (struct gids_private_data*) card->drv_data; + u8 cardcf[6]; + int r; + size_t cardcfsize = sizeof(cardcf); + r = gids_read_gidsfile_without_cache(card, data->masterfile, data->masterfilesize, "", "cardcf", cardcf, &cardcfsize); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the cardcf"); + + if (file) { + short filefreshness = cardcf[4] + cardcf[5] * 0x100; + filefreshness++; + cardcf[4] = filefreshness & 0xFF; + cardcf[5] = (filefreshness>>8) & 0xFF; + } + if (container) { + short containerfreshness = cardcf[2] + cardcf[3] * 0x100; + containerfreshness++; + cardcf[2] = containerfreshness & 0xFF; + cardcf[3] = (containerfreshness>>8) & 0xFF; + } + r = gids_write_gidsfile_without_cache(card, data->masterfile, data->masterfilesize, "", "cardcf", cardcf, 6); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cardcf file"); + return r; +} + +// read a file +static int gids_read_gidsfile(sc_card_t* card, char *directory, char *filename, u8* response, size_t *responselen) { + struct gids_private_data* privatedata = (struct gids_private_data*) card->drv_data; + int r; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + if (privatedata->masterfilesize == sizeof(privatedata->masterfile)) { + r = gids_read_masterfile(card); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the masterfile"); + } + r = gids_read_gidsfile_without_cache(card, privatedata->masterfile, privatedata->masterfilesize, + directory, filename, response, responselen); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to read the file"); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); +} + +// check for the existance of a file +static int gids_does_file_exists(sc_card_t *card, char* directory, char* filename) { + struct gids_private_data* privatedata = (struct gids_private_data*) card->drv_data; + int fileIdentifier, dataObjectIdentifier; + return gids_get_identifiers(card, privatedata->masterfile, privatedata->masterfilesize, directory, filename, + &fileIdentifier, &dataObjectIdentifier); +} + +// write a file already existing +static int gids_write_gidsfile(sc_card_t* card, char *directory, char *filename, u8* data, size_t datalen) { + struct gids_private_data* privatedata = (struct gids_private_data*) card->drv_data; + int r; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + r = gids_update_cardcf(card, 1, 0); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cache file"); + + r = gids_write_gidsfile_without_cache(card, privatedata->masterfile, privatedata->masterfilesize, + directory, filename, data, datalen); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the file"); + if (strcmp(directory, "mscp") == 0 && strcmp(filename, "cmapfile") == 0) { + // update the cmapfile cache + privatedata->cmapfilesize = datalen; + memcpy(privatedata->cmapfile, data, datalen); + } + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,r); +} + +// read the cmapfile (container description) +static int gids_read_cmapfile(sc_card_t* card) { + struct gids_private_data* data = (struct gids_private_data*) card->drv_data; + int r = SC_SUCCESS; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + data->cmapfilesize = sizeof(data->cmapfile); + r = gids_read_gidsfile(card, "mscp", "cmapfile", data->cmapfile, &data->cmapfilesize); + if (r<0) { + data->cmapfilesize = sizeof(data->cmapfile); + } + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the cmapfile"); + return r; +} + +// create a file record in the masterfile +static int gids_create_file(sc_card_t *card, char* directory, char* filename) { + int r; + u8 masterfilebuffer[MAX_GIDS_FILE_SIZE]; + size_t masterfilebuffersize; + struct gids_private_data* privatedata = (struct gids_private_data*) card->drv_data; + int fileIdentifier, dataObjectIdentifier; + size_t records; + int offset; + gids_mf_record_t* record; + + r = gids_find_available_DO(card, privatedata->masterfile, privatedata->masterfilesize, &fileIdentifier, &dataObjectIdentifier); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to find an empty DO"); + + memcpy(masterfilebuffer, privatedata->masterfile, privatedata->masterfilesize); + masterfilebuffersize = privatedata->masterfilesize + sizeof(gids_mf_record_t); + if (masterfilebuffersize > MAX_GIDS_FILE_SIZE) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_ENOUGH_MEMORY); + } + + records = ((privatedata->masterfilesize -1) / sizeof(gids_mf_record_t)); + offset = 1 + sizeof(gids_mf_record_t) * records; + memcpy(masterfilebuffer + offset + sizeof(gids_mf_record_t), masterfilebuffer + offset, + privatedata->masterfilesize - offset); + memset(masterfilebuffer + offset, 0, sizeof(gids_mf_record_t)); + record = (gids_mf_record_t*) (masterfilebuffer + offset); + strncpy(record->directory, directory, 8); + strncpy(record->filename, filename, 8); + record->fileIdentifier = fileIdentifier; + record->dataObjectIdentifier = dataObjectIdentifier; + + r = gids_update_cardcf(card, 1, 0); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cardcf"); + + r = gids_put_DO(card, MF_FI, MF_DO, masterfilebuffer, masterfilebuffersize); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the masterfile"); + + memcpy(privatedata->masterfile, masterfilebuffer, masterfilebuffersize); + privatedata->masterfilesize = masterfilebuffersize; + return r; +} + +// CERTIFICATE HANDLING FUNCTIONS +//////////////////////////////////////////////////// + +// prepare a sc_path structure given a file identifier & DO +// this will be an input of the gids_read_public_key function +static int gids_build_certificate_path(sc_card_t* card, unsigned char containerindex, unsigned char issignatureonly,sc_path_t* cpath) { + struct gids_private_data* data = (struct gids_private_data*) card->drv_data; + int r, fileIdentifier, dataObjectIdentifier; + char file[9]; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + if (issignatureonly) { + snprintf(file, 9, "ksc%02X", containerindex); + } else { + snprintf(file, 9, "kxc%02X", containerindex); + } + r = gids_get_identifiers(card, data->masterfile, data->masterfilesize, "mscp", file, &fileIdentifier, &dataObjectIdentifier); + if (r < 0) return SC_ERROR_OBJECT_NOT_FOUND; + + memset(cpath, 0, sizeof(sc_path_t)); + cpath->type = SC_PATH_TYPE_PATH; + cpath->len = 4; + cpath->value[0] = (u8) ((fileIdentifier >> 8) & 0xFF); + cpath->value[1] = (u8) fileIdentifier & 0xFF; + cpath->value[2] = (u8) ((dataObjectIdentifier >> 8) & 0xFF); + cpath->value[3] = (u8) dataObjectIdentifier & 0xFF; + cpath->count = -1; + return SC_SUCCESS; +} + +// PIN HANDLING FUNCTIONS +//////////////////////////////////////////////////// + +// get the pin status +static int gids_get_pin_status(sc_card_t *card, int pinreference, int *tries_left, int *max_tries) { + int dataObjectIdentifier, r; + u8 buffer[100]; + const u8* p; + size_t buffersize = sizeof(buffer); + size_t datasize; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + if (tries_left) *tries_left = -1; + if (max_tries) *max_tries = -1; + switch(pinreference) { + case GIDS_PIN_IDENTIFIER: + dataObjectIdentifier = GIDS_PIN_STATUS_OBJECT_IDENTIFIER; + break; + case GIDS_PUK_IDENTIFIER: + dataObjectIdentifier = GIDS_PUK_STATUS_OBJECT_IDENTIFIER; + break; + default: + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_FOUND); + } + r = gids_get_DO(card, GIDS_APPLET_EFID, dataObjectIdentifier, buffer, &buffersize); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the masterfile"); + + p = sc_asn1_find_tag(card->ctx, buffer, sizeof(buffer), GIDS_TRY_COUNTER_OLD_TAG, &datasize); + if (p && datasize == 1) { + if (tries_left) + *tries_left = p[0]; + } + p = sc_asn1_find_tag(card->ctx, buffer, sizeof(buffer), GIDS_TRY_COUNTER_TAG, &datasize); + if (p && datasize == 1) { + if (tries_left) + *tries_left = p[0]; + } + p = sc_asn1_find_tag(card->ctx, buffer, sizeof(buffer), GIDS_TRY_LIMIT_TAG, &datasize); + if (p && datasize == 1) { + if (tries_left) + *max_tries = p[0]; + } + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "Pin information for PIN 0x%x: triesleft=%d trieslimit=%d\n", pinreference, (tries_left?*tries_left:-1), (max_tries?*max_tries:-1)); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); +} + +static int gids_match_card(sc_card_t * card) +{ + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + int r,i; + size_t resplen = sizeof(rbuf); + const u8 *tag; + size_t taglen; + const u8 *aid; + size_t aidlen; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* Detect by selecting applet */ + r = gids_select_aid(card, gids_aid.value, gids_aid.len, rbuf, &resplen); + if (r<0) return 0; + + card->type = SC_CARD_TYPE_GIDS_GENERIC; + if (resplen > 2) { + tag = sc_asn1_find_tag(card->ctx, rbuf, resplen, GIDS_APPLICATION_TEMPLATE_TAG, &taglen); + if (tag != NULL) { + aid = sc_asn1_find_tag(card->ctx, tag, taglen, GIDS_APPLICATION_AID_TAG, &aidlen); + if (aid != NULL ) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"found AID"); + for (i = 0; gids_aids[i].len_long != 0; i++) { + if ( aidlen > gids_aids[i].len_long && memcmp(aid, gids_aids[i].value, + gids_aids[i].len_long) == 0) { + card->type = gids_aids[i].enumtag; + break; + } + } + } + } + } + + return 1; +} + + +// extract the serial number from the cardid file +static int gids_get_serialnr(sc_card_t * card, sc_serial_number_t * serial) +{ + int r; + u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE]; + size_t buffersize; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + buffersize = sizeof(buffer); + r = gids_read_gidsfile(card, "", "cardid", buffer, &buffersize); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to read cardid"); + + if (SC_MAX_SERIALNR < buffersize) + { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + } + + /* cache serial number */ + card->serialnr.len = buffersize; + memcpy(card->serialnr.value, buffer, card->serialnr.len); + + /* return cached serial number */ + if (serial) + memcpy(serial, &card->serialnr, sizeof(*serial)); + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); +} + +// initialize the driver +static int gids_init(sc_card_t * card) +{ + unsigned long flags; + struct gids_private_data *data; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + // cache some data in memory + data = (struct gids_private_data*) calloc(1, sizeof(struct gids_private_data)); + if (!data) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_MEMORY_FAILURE); + } + memset(data, 0, sizeof(struct gids_private_data)); + card->drv_data = data; + // invalidate the master file and cmap file cache + data->cmapfilesize = sizeof(data->cmapfile); + data->masterfilesize = sizeof(data->masterfile); + + /* supported RSA keys and how padding is done */ + flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE | SC_ALGORITHM_ONBOARD_KEY_GEN | SC_ALGORITHM_RSA_RAW; + /* fix me: add other algorithms when the gids specification will tell how to extract the algo id from the FCP */ + _sc_card_add_rsa_alg(card, 1024, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); + return SC_SUCCESS; +} + +// cleanup +static int gids_finish(sc_card_t *card) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + /* free the private data */ + if (card->drv_data) { + free(card->drv_data); + card->drv_data = NULL; + } + return 0; +} + +//see 12.5.3.1 Cryptographic Mechanism Identifier for Key with CRT +// the cmap file is used to detect the key algorithm / size +static int gids_get_crypto_identifier_from_key_ref(sc_card_t *card, const unsigned char keyref, unsigned char *cryptoidentifier) { + struct gids_private_data *data = (struct gids_private_data *) card->drv_data; + PCONTAINER_MAP_RECORD records = (PCONTAINER_MAP_RECORD) data->cmapfile; + int recordsnum = (int) (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); + int index = keyref - GIDS_FIRST_KEY_IDENTIFIER; + if (index >= recordsnum) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + } + if (records[index].wKeyExchangeKeySizeBits == 1024 || records[index].wSigKeySizeBits == 1024) { + *cryptoidentifier = GIDS_RSA_1024_IDENTIFIER; + return SC_SUCCESS; + } + if (records[index].wKeyExchangeKeySizeBits == 2048 || records[index].wSigKeySizeBits == 2048) { + *cryptoidentifier = GIDS_RSA_2048_IDENTIFIER; + return SC_SUCCESS; + } + if (records[index].wKeyExchangeKeySizeBits == 3072 || records[index].wSigKeySizeBits == 3072) { + *cryptoidentifier = GIDS_RSA_3072_IDENTIFIER; + return SC_SUCCESS; + } + if (records[index].wKeyExchangeKeySizeBits == 4096 || records[index].wSigKeySizeBits == 4096) { + *cryptoidentifier = GIDS_RSA_4096_IDENTIFIER; + return SC_SUCCESS; + } + if (records[index].wKeyExchangeKeySizeBits == 192 || records[index].wSigKeySizeBits == 192) { + *cryptoidentifier = GIDS_ECC_192_IDENTIFIER; + return SC_SUCCESS; + } + if (records[index].wKeyExchangeKeySizeBits == 224 || records[index].wSigKeySizeBits == 224) { + *cryptoidentifier = GIDS_ECC_224_IDENTIFIER; + return SC_SUCCESS; + } + if (records[index].wKeyExchangeKeySizeBits == 256 || records[index].wSigKeySizeBits == 256) { + *cryptoidentifier = GIDS_ECC_256_IDENTIFIER; + return SC_SUCCESS; + } + if (records[index].wKeyExchangeKeySizeBits == 384 || records[index].wSigKeySizeBits == 384) { + *cryptoidentifier = GIDS_ECC_384_IDENTIFIER; + return SC_SUCCESS; + } + if (records[index].wKeyExchangeKeySizeBits == 521 || records[index].wSigKeySizeBits == 521) { + *cryptoidentifier = GIDS_ECC_521_IDENTIFIER; + return SC_SUCCESS; + } + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); +} + +// same here +static u8 gids_get_crypto_identifier_from_prkey_info(struct sc_pkcs15_prkey_info *key_info) { + if (key_info->modulus_length > 0) { + if (key_info->modulus_length == 1024) { + return GIDS_RSA_1024_IDENTIFIER; + } + if (key_info->modulus_length == 2048) { + return GIDS_RSA_2048_IDENTIFIER; + } + if (key_info->modulus_length == 3072) { + return GIDS_RSA_3072_IDENTIFIER; + } + if (key_info->modulus_length == 4096) { + return GIDS_RSA_4096_IDENTIFIER; + } + return 0; + } else { + return 0; + } +} + +// GIDS implementation of set security environment +static int gids_set_security_env(sc_card_t *card, + const sc_security_env_t *env, + int se_num) +{ + struct sc_apdu apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + u8 *p; + int r, locked = 0; + + assert(card != NULL && env != NULL); + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_MANAGE_SECURITY_ENVIRONMENT, P1_DECIPHERMENT_INTERNAL_AUTHENTICATE_KEY_AGREEMENT, 0); + switch (env->operation) { + case SC_SEC_OPERATION_DECIPHER: + apdu.p2 = P2_DECIPHERMENT; + break; + case SC_SEC_OPERATION_SIGN: + apdu.p2 = P2_DIGITAL_SIGNATURE; + break; + default: + return SC_ERROR_INVALID_ARGUMENTS; + } + p = sbuf; + if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { + return SC_ERROR_NOT_SUPPORTED; + } else { + // ALG REF is mandatory + *p++ = 0x80; /* algorithm reference */ + *p++ = 0x01; + gids_get_crypto_identifier_from_key_ref(card,env->key_ref[0],p); + if (env->operation == SC_SEC_OPERATION_DECIPHER) { + *p++ |= 0x40; + } else { + *p++ |= 0x50; + } + } + if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT)) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + } + if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC) + *p++ = 0x83; + else + *p++ = 0x84; + *p++ = (u8) env->key_ref_len; + assert(sizeof(sbuf) - (p - sbuf) >= env->key_ref_len); + memcpy(p, env->key_ref, env->key_ref_len); + p += env->key_ref_len; + + r = (int) (p - sbuf); + apdu.lc = r; + apdu.datalen = r; + apdu.data = sbuf; + if (se_num > 0) { + r = sc_lock(card); + LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); + locked = 1; + } + if (apdu.datalen != 0) { + r = sc_transmit_apdu(card, &apdu); + if (r) { + sc_log(card->ctx, "%s: APDU transmit failed", sc_strerror(r)); + goto err; + } + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) { + sc_log(card->ctx, "%s: Card returned error", sc_strerror(r)); + goto err; + } + } + if (se_num <= 0) + return 0; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_MANAGE_SECURITY_ENVIRONMENT, 0xF2, se_num); + r = sc_transmit_apdu(card, &apdu); + sc_unlock(card); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + return sc_check_sw(card, apdu.sw1, apdu.sw2); +err: + if (locked) + sc_unlock(card); + return r; +} + +// deauthenticate all pins +static int gids_logout(sc_card_t *card) +{ + struct sc_apdu apdu; + int r; + assert(card && card->ctx); + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + + // use the special PIN to deauthenticate + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, INS_VERIFY, 0x00, P2_PIN_DEAUTHENTICATE); + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} + +// read a public key +static int gids_read_public_key (struct sc_card *card , unsigned int algorithm, + struct sc_path * path, unsigned key_reference, unsigned modulus_length, + unsigned char **response, size_t *responselen) { + + struct sc_pkcs15_pubkey_rsa rsa_key; + sc_apdu_t apdu; + size_t tlen, len; + const u8* keytemplate; + const u8* keydata; + int r; + u8 data[] = {0x70, 0x08, // retrieve key + 0x84, 0x01, key_reference, // key reference + 0xA5, 0x03, 0x7F, 0x49, 0x80 // key value template: only public key + }; + u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE]; + size_t buffersize = sizeof(buffer); + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "Got args: key_reference=%x, response=%x, responselen=%d\n", + key_reference, response, responselen ? *responselen : 0); + + sc_format_apdu(card, &apdu, + response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, INS_GET_DATA, 0x3F, 0xFF); + apdu.lc = sizeof(data); + apdu.data = data; + apdu.datalen = sizeof(data); + apdu.resp = buffer; + apdu.resplen = buffersize; + apdu.le = 256; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read public key failed"); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + buffersize = apdu.resplen; + + keytemplate = sc_asn1_find_tag(card->ctx, buffer, buffersize, GIDS_PUBKEY_TAG, &tlen); + if (keytemplate == NULL) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid public key data: missing tag"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + + keydata = sc_asn1_find_tag(card->ctx, keytemplate, tlen, GIDS_PUBKEY_TAG_MODULUS, &len); + if (keydata != NULL) { + rsa_key.modulus.data = (u8*) keydata; + rsa_key.modulus.len = len; + } + + keydata = sc_asn1_find_tag(card->ctx, keytemplate, tlen, GIDS_PUBKEY_TAG_EXPONENT, &len); + if (keydata != NULL) { + rsa_key.exponent.data = (u8*) keydata; + rsa_key.exponent.len = len; + } + + if (rsa_key.exponent.len && rsa_key.modulus.len) { + r = sc_pkcs15_encode_pubkey_rsa(card->ctx, &rsa_key, response, responselen); + LOG_TEST_RET(card->ctx, r, "failed to read public key: cannot encode RSA public key"); + } else { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "it is not a known public key"); + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); + } + + if (response && responselen) + sc_log(card->ctx, "encoded public key: %s", sc_dump_hex(*response, *responselen)); + + return SC_SUCCESS; +} + +// emulate a filesystem given EF and DO +static int gids_select_file(sc_card_t *card, const struct sc_path *in_path, + struct sc_file **file_out) { + struct sc_file *file = NULL; + struct sc_context *ctx = card->ctx; + struct gids_private_data *data = (struct gids_private_data *) card->drv_data; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + + data->state = GIDS_STATE_NONE; + if (in_path->len == 4 && in_path->value[0] == 0xA0) { + // is it a DO pseudo file ? + // yes, succeed + data->currentEFID = in_path->value[1] + (in_path->value[0]<<8); + data->currentDO = in_path->value[3] + (in_path->value[2]<<8); + + file = sc_file_new(); + if (file == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + file->path = *in_path; + file->type = SC_FILE_TYPE_WORKING_EF; + file->ef_structure = SC_FILE_EF_TRANSPARENT; + file->size = SC_MAX_EXT_APDU_BUFFER_SIZE; + *file_out = file; + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + } else { + data->currentDO = 0; + data->currentEFID = 0; + return iso_ops->select_file(card, in_path, file_out); + } +} + +static int gids_get_pin_policy(struct sc_card *card, struct sc_pin_cmd_data *data) { + int r; + if (data->pin_type != SC_AC_CHV) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS); + } + r = gids_get_pin_status(card, data->pin_reference, &(data->pin1.tries_left), &(data->pin1.max_tries)); + LOG_TEST_RET(card->ctx, r, "gids_get_pin_status failed"); + data->pin1.max_length = 16; + data->pin1.min_length = 4; + data->pin1.stored_length = 0; + data->pin1.encoding = SC_PIN_ENCODING_ASCII; + data->pin1.offset = 5; + return SC_SUCCESS; +} + +static int +gids_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) { + if (data->cmd == SC_PIN_CMD_GET_INFO) { + return gids_get_pin_policy(card, data); + } else { + return iso_ops->pin_cmd(card, data, tries_left); + } +} + +// used to read existing certificates +static int gids_read_binary(sc_card_t *card, unsigned int offset, + unsigned char *buf, size_t count, unsigned long flags) { + struct gids_private_data *data = (struct gids_private_data *) card->drv_data; + struct sc_context *ctx = card->ctx; + int r; + int size; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + + if (! data->currentDO || ! data->currentEFID) { + LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); + } + if (data->state != GIDS_STATE_READ_DATA_PRESENT) { + // this function is called to read the certificate only + u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE]; + size_t buffersize = sizeof(buffer); + r = gids_get_DO(card, data->currentEFID, data->currentDO, buffer, &(buffersize)); + if (r <0) return r; + if (buffersize < 4) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA); + } + if (buffer[0] == 1 && buffer[1] == 0) { + size_t expectedsize = buffer[2] + buffer[3] * 0x100; + data->buffersize = sizeof(data->buffer); + r = sc_decompress(data->buffer, &(data->buffersize), buffer+4, buffersize-4, COMPRESSION_ZLIB); + if (r != SC_SUCCESS) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Zlib error: %d", r); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + } + if (data->buffersize != expectedsize) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "expected size: %d real size: %d", expectedsize, data->buffersize); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA); + } + } else { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unknown compression method %d", buffer[0] + (buffer[1] <<8)); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA); + } + data->state = GIDS_STATE_READ_DATA_PRESENT; + } + if (offset >= data->buffersize) { + return 0; + } + size = (int) MIN((data->buffersize - offset), count); + memcpy(buf, data->buffer + offset, size); + return size; +} + +// refresh the internal caches and return the number of containers +static int +gids_get_all_containers(sc_card_t* card, size_t *recordsnum) { + int r; + struct gids_private_data *privatedata = (struct gids_private_data *) card->drv_data; + r = gids_read_masterfile(card); + LOG_TEST_RET(card->ctx, r, "unable to read the masterfile"); + r = gids_read_cmapfile(card); + LOG_TEST_RET(card->ctx, r, "unable to read the cmapfile"); + *recordsnum = (privatedata ->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); + return SC_SUCCESS; +} + +// return the detail about a container to emulate a pkcs15 card +static int +gids_get_container_detail(sc_card_t* card, sc_cardctl_gids_get_container_t* container) { + PCONTAINER_MAP_RECORD records = NULL; + struct gids_private_data *privatedata = (struct gids_private_data *) card->drv_data; + size_t recordsnum, num, i; + records = (PCONTAINER_MAP_RECORD) privatedata ->cmapfile; + recordsnum = (privatedata ->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); + + num = container->containernum ; + if (num >= recordsnum) { + return SC_ERROR_OBJECT_NOT_FOUND; + } + memset(container, 0, sizeof(sc_cardctl_gids_get_container_t)); + container->containernum = num; + + if (!records[num].bFlags & CONTAINER_MAP_VALID_CONTAINER) { + return SC_SUCCESS; + } + // ignore problematic containers + if (records[num].wKeyExchangeKeySizeBits > 0 && records[num].wSigKeySizeBits > 0) { + return SC_SUCCESS; + } + if (records[num].wKeyExchangeKeySizeBits == 0 && records[num].wSigKeySizeBits == 0) { + return SC_SUCCESS; + } + for (i = 0; i < MAX_CONTAINER_NAME_LEN; i++) { + container->label[i] = (char) records[num].wszGuid[i]; + } + container->label[MAX_CONTAINER_NAME_LEN] = 0; + + container->module_length = MAX(records[num].wKeyExchangeKeySizeBits, records[num].wSigKeySizeBits); + container->prvusage = SC_PKCS15_PRKEY_USAGE_SIGN; + container->pubusage = SC_PKCS15_PRKEY_USAGE_VERIFY; + if (records[num].wKeyExchangeKeySizeBits > 0) { + container->prvusage |= SC_PKCS15_PRKEY_USAGE_DECRYPT; + container->pubusage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT; + } + + // do not check for return code, typically if there is no certificate associated to the key + gids_build_certificate_path(card, (unsigned char) num, (records[num].wSigKeySizeBits > 0), &(container->certificatepath)); + + return SC_SUCCESS; +} + +// find a new key reference +static int +gids_select_key_reference(sc_card_t *card, sc_pkcs15_prkey_info_t* key_info) { + struct gids_private_data *data = (struct gids_private_data *) card->drv_data; + PCONTAINER_MAP_RECORD records = (PCONTAINER_MAP_RECORD) data->cmapfile; + size_t recordsnum; + int r; + char ch_tmp[10]; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + // refresh the cached data in case some thing has been modified + r = gids_read_masterfile(card); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read masterfile failed"); + r = gids_read_cmapfile(card); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read cmapfile failed"); + + recordsnum = (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); + + if (!key_info->key_reference) { + // new key + size_t i; + // search for a key number not used anymore + for (i = 0; i < recordsnum; i++) { + if (!(records[i].bFlags & CONTAINER_MAP_VALID_CONTAINER)) { + key_info->key_reference = (int) (GIDS_FIRST_KEY_IDENTIFIER + i); + return SC_SUCCESS; + } + } + // use a new key number + if (recordsnum > GIDS_MAX_CONTAINER) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_ENOUGH_MEMORY); + } + key_info->key_reference = (int) (GIDS_FIRST_KEY_IDENTIFIER + recordsnum); + } else { + // key was specified. Search if the key can be used + size_t i = key_info->key_reference - GIDS_FIRST_KEY_IDENTIFIER; + if (i > GIDS_MAX_CONTAINER) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid key ref %d", key_info->key_reference); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + } + if (i > recordsnum) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "container num is not allowed %d %d", i, recordsnum); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); + } + } + snprintf(ch_tmp, sizeof(ch_tmp), "3FFFB0%02X", key_info->key_reference); + sc_format_path(ch_tmp, &(key_info->path)); + return SC_SUCCESS; +} + +// perform the creation of the key file +// try to mimic the GIDS minidriver key permission +static int gids_perform_create_keyfile(sc_card_t *card, u8 keytype, u8 kid, u8 algid) { + struct sc_apdu apdu; + int r; + u8 keyexchange[] = {0x62,0x47, + 0x82,0x01,0x18, // file type + 0x83,0x02,0xB0,kid, // key id = 81 + 0x8C,0x05,0x8F,0x10,0x10,0x10,0x00, // security + 0xA5,0x37, + 0xB8,0x09, // confidentiality + 0x80,0x01,algid, //algo: rsa without padding + 0x83,0x01,kid, // key id + 0x95,0x01,0x40, // usage + 0xB8,0x09, // confidentiality + 0x80,0x01,0x80 + algid, // RSAES-OAEP padding + 0x83,0x01,kid, + 0x95,0x01,0x40, + 0xB8,0x09, // confidentiality + 0x80,0x01,0x40 + algid, // RSAES-PKCS1-v1_5 padding + 0x83,0x01,kid, + 0x95,0x01,0x40, + 0xB6,0x09, // signature + 0x80,0x01,0x10 + algid, // Full SHA off-card authorized + 0x83,0x01,kid, + 0x95,0x01,0x40, + 0xB6,0x09, // signature + 0x80,0x01,0x50 + algid, // RSASSA PKCS1-v 1_5 padding scheme (for RSA only; otherwise, RFU) + 0x83,0x01,kid, + 0x95,0x01,0x40 + }; + u8 sign[] = {0x62,0x26, + 0x82,0x01,0x18, // file type + 0x83,0x02,0xB0,kid, // key id = 81 + 0x8C,0x05,0x8F,0x10,0x10,0x10,0x00, // security + 0xA5,0x16, + 0xB6,0x09, // signature + 0x80,0x01,0x10+ algid, // Full SHA off-card authorized + 0x83,0x01,0x83, // key id + 0x95,0x01,0x40, // usage + 0xB6,0x09, // signature + 0x80,0x01,0x50 + algid, // RSASSA PKCS1-v 1_5 padding scheme (for RSA only; otherwise, RFU) + 0x83,0x01,0x83, + 0x95,0x01,0x40}; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + // create the key file + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_CREATE_FILE, 0x00, 0x00); + if (keytype == 1) { + apdu.lc = sizeof(keyexchange); + apdu.datalen = sizeof(keyexchange); + apdu.data = keyexchange; + } else if (keytype == 2) { + apdu.lc = sizeof(sign); + apdu.datalen = sizeof(sign); + apdu.data = sign; + } else { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + } + + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, r, "Card returned error"); + + // activate file + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, INS_ACTIVATE_FILE, 0x00, 0x00); + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "ACTIVATE_FILE returned error"); + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); +} + +// perform the creation of the keyfile and its registration in the cmapfile and keymap file +static int gids_create_keyfile(sc_card_t *card, sc_pkcs15_object_t *object) { + + int r; + u8 keytype; + struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; + u8 kid = key_info->key_reference; + u8 algid = gids_get_crypto_identifier_from_prkey_info(key_info); + u8 cmapbuffer[MAX_GIDS_FILE_SIZE]; + size_t cmapbuffersize = 0; + u8 keymapbuffer[MAX_GIDS_FILE_SIZE]; + size_t keymapbuffersize = 0; + size_t keymaprecordnum = 0; + struct gids_private_data *data = (struct gids_private_data *) card->drv_data; + size_t recordnum; + size_t containernum = key_info->key_reference - GIDS_FIRST_KEY_IDENTIFIER; + PCONTAINER_MAP_RECORD records = ((PCONTAINER_MAP_RECORD) cmapbuffer) + containernum; + struct gids_keymap_record* keymaprecord = NULL; + int i; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + // sanity check + assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY); + + if (!algid) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + } + + // masterfile & cmapfile have been refreshed in gids_perform_create_keyfile + + recordnum = (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); + + // sanity check + if (containernum > recordnum || containernum > GIDS_MAX_CONTAINER) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + + // refresh the key map file + keymapbuffersize = sizeof(keymapbuffer); + r = gids_get_DO(card, KEYMAP_FI, KEYMAP_DO, keymapbuffer, &keymapbuffersize); + if (r<0) { + // the keymap DO should be present if the cmapfile is not empty + if (recordnum > 0) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + } + // else can be empty if not record + keymaprecordnum = 0; + keymapbuffersize = 0; + } else { + keymaprecordnum = (keymapbuffersize - 1) / sizeof(struct gids_keymap_record); + if (keymaprecordnum != recordnum) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL , "Error: Unable to create the key file because the keymap and cmapfile are inconsistent"); + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL , "keymaprecordnum = %u recordnum = %u", (unsigned long) keymaprecordnum, (unsigned long) recordnum); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + } + } + + + // prepare the cmap & keymap buffer + if (containernum == recordnum) { + // reserve space on the cmap file + memset(cmapbuffer, 0, sizeof(cmapbuffer)); + memcpy(cmapbuffer, data->cmapfile, data->cmapfilesize); + cmapbuffersize = data->cmapfilesize + sizeof(CONTAINER_MAP_RECORD); + r = gids_write_gidsfile(card, "mscp", "cmapfile", cmapbuffer, cmapbuffersize); + LOG_TEST_RET(card->ctx, r, "unable to reserve space on the cmapfile"); + + if (keymapbuffersize == 0) { + keymapbuffersize = 1; + keymapbuffer[0] = 1; + } + keymapbuffersize += sizeof(struct gids_keymap_record); + } else { + memcpy(cmapbuffer, data->cmapfile, data->cmapfilesize); + cmapbuffersize = data->cmapfilesize; + } + keymaprecord = ((struct gids_keymap_record*)(keymapbuffer +1)) + containernum; + + memset(records, 0, sizeof(CONTAINER_MAP_RECORD)); + memset(keymaprecord, 0, sizeof(struct gids_keymap_record)); + + if (key_info->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT) { + keytype = 1; // AT_KEYEXCHANGE + records->wKeyExchangeKeySizeBits = (unsigned short) key_info->modulus_length; + keymaprecord->keytype = GIDS_KEY_TYPE_AT_KEYEXCHANGE; + } else if (key_info->usage & SC_PKCS15_PRKEY_USAGE_SIGN) { + keytype = 2; // AT_SIGNATURE + records->wSigKeySizeBits = (unsigned short) key_info->modulus_length; + keymaprecord->keytype = GIDS_KEY_TYPE_AT_SIGNATURE; + } else { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); + } + + //the GIDS card must have unique container names + // avoid the problem with the default label by making it unique + if (strcmp(DEFAULT_PRIVATE_KEY_LABEL, object->label) == 0 && strlen(DEFAULT_PRIVATE_KEY_LABEL) + 3 < MAX_CONTAINER_NAME_LEN) { + char addition[4] = " 00"; + addition[1] += containernum % 10; + addition[2] += (containernum < 0xFF) / 10; + strcat(object->label, addition); + } + + // convert char to wchar + for(i = 0; i < MAX_CONTAINER_NAME_LEN && object->label[i]; i++) { + records->wszGuid[i] = object->label[i]; + } + + // TODO: check if a container with the same name already exists and prevent is creation or change its name + + records->bFlags = CONTAINER_MAP_VALID_CONTAINER; + if (recordnum == 0) { + records->bFlags |= CONTAINER_MAP_DEFAULT_CONTAINER; + } + keymaprecord->algid = algid; + keymaprecord->state = 1; + keymaprecord->unknownWithFFFF = (unsigned short) (-1); + keymaprecord->keyref = 0xB000 + kid; + r = gids_perform_create_keyfile(card, keytype, kid, algid); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to create the key file"); + + r = gids_update_cardcf(card, 0, 1); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cardcf file regarding container"); + r = gids_put_DO(card, KEYMAP_FI, KEYMAP_DO, keymapbuffer, keymapbuffersize); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the keymap file"); + + r = gids_write_gidsfile(card, "mscp", "cmapfile", cmapbuffer, cmapbuffersize); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the cmap file after the container creation"); + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); +} + +// generate a key on an existing container +static int gids_generate_key(sc_card_t *card, sc_pkcs15_object_t *object, struct sc_pkcs15_pubkey* pubkey) { + + struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; + u8 kid = key_info->key_reference; + u8 algid = gids_get_crypto_identifier_from_prkey_info(key_info); + struct sc_apdu apdu; + u8 generatekey[] = {0xAC, 0x06, // CRT template + 0x80, 0x01, algid, // algorithm + 0x83, 0x01, kid // key reference + }; + int r; + u8 *buffer = NULL; + size_t buffersize = 0; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY); + + if ((key_info->key_reference > GIDS_FIRST_KEY_IDENTIFIER + GIDS_MAX_CONTAINER) || (kid < GIDS_FIRST_KEY_IDENTIFIER)) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_DATA); + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_GENERATE_ASYMECTRIC_KEY_PAIR, 0x00, 0x00); + apdu.lc = sizeof(generatekey); + apdu.datalen = sizeof(generatekey); + apdu.data = generatekey; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "generate key returned error"); + + r = gids_read_public_key(card, 0, NULL, kid, 0, &buffer, &buffersize); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "read public key returned error"); + r = sc_pkcs15_decode_pubkey(card->ctx, pubkey, buffer, buffersize); + if (buffer) + free(buffer); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); +} + +// import the key in an existing container +static int gids_import_key(sc_card_t *card, sc_pkcs15_object_t *object, sc_pkcs15_prkey_t *key) { + struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; + int version = 0; + int keytype = 2; // RSA + u8 kid = key_info->key_reference; + size_t len = 1; + int encryptkeyref = 0; //NONE + int r; + u8* buffer = NULL; + size_t buflen = 0; + + struct sc_asn1_entry asn1_key_usage_template[] = { + { "keyReference", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING | SC_ASN1_CTX, 0, NULL, NULL }, + { "KeyValueTemplate", SC_ASN1_STRUCT, SC_ASN1_TAG_NULL | SC_ASN1_CTX | SC_ASN1_CONS, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + + struct sc_asn1_entry asn1_key_value_template[] = { + { "keyType", SC_ASN1_INTEGER, SC_ASN1_TAG_BIT_STRING | SC_ASN1_CTX, 0, NULL, NULL }, + { "encryptKeyRef", SC_ASN1_INTEGER, SC_ASN1_TAG_OCTET_STRING | SC_ASN1_CTX, 0, NULL, NULL }, + { "keyValue", SC_ASN1_STRUCT, SC_ASN1_TAG_OBJECT_DESCRIPTOR | SC_ASN1_CTX, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + + struct sc_asn1_entry asn1_key_data[] = { + { "keyData", SC_ASN1_STRUCT, SC_ASN1_SEQUENCE | SC_ASN1_CONS, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + + struct sc_asn1_entry asn1_rsa_priv_coefficients_gids[] = { + { "version", SC_ASN1_INTEGER, SC_ASN1_TAG_INTEGER, 0, NULL, NULL }, + { "modulus", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, + { "publicExponent", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, + { "privateExponent", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, + { "p", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, + { "q", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, + { "dmp1", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, + { "dmq1", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, + { "iqmp", SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, SC_ASN1_ALLOC, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY); + + if (object->type != SC_PKCS15_TYPE_PRKEY_RSA || key->algorithm != SC_ALGORITHM_RSA) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "GIDS supports RSA keys only (but may support ECC one day)."); + return SC_ERROR_NOT_SUPPORTED; + } + if (!key->u.rsa.dmp1.len || !key->u.rsa.dmq1.len || !key->u.rsa.iqmp.len) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "GIDS needs dmp1 & dmq1 & iqmp"); + return SC_ERROR_NOT_SUPPORTED; + } + sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 0, &version, NULL, 1); + sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 1, key->u.rsa.modulus.data, &key->u.rsa.modulus.len, 1); + sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 2, key->u.rsa.exponent.data, &key->u.rsa.exponent.len, 1); + sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 3, key->u.rsa.d.data, &key->u.rsa.d.len, 1); + sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 4, key->u.rsa.p.data, &key->u.rsa.p.len, 1); + sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 5, key->u.rsa.q.data, &key->u.rsa.q.len, 1); + sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 6, key->u.rsa.dmp1.data, &key->u.rsa.dmp1.len, 1); + sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 7, key->u.rsa.dmq1.data, &key->u.rsa.dmq1.len, 1); + sc_format_asn1_entry(asn1_rsa_priv_coefficients_gids + 8, key->u.rsa.iqmp.data, &key->u.rsa.iqmp.len, 1); + + sc_format_asn1_entry(asn1_key_data + 0, asn1_rsa_priv_coefficients_gids, NULL, 1); + + sc_format_asn1_entry(asn1_key_value_template + 0, &keytype, NULL, 1); + sc_format_asn1_entry(asn1_key_value_template + 1, &encryptkeyref, NULL, 1); + sc_format_asn1_entry(asn1_key_value_template + 2, asn1_key_data, NULL, 1); + + sc_format_asn1_entry(asn1_key_usage_template + 0, &kid, &len, 1); + sc_format_asn1_entry(asn1_key_usage_template + 1, asn1_key_value_template, NULL, 1); + + r = sc_asn1_encode(card->ctx, asn1_key_usage_template, &buffer, &buflen); + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to encode the private key"); + + r = gids_put_DO(card, GIDS_APPLET_EFID, GIDS_PUT_KEY_DO, buffer, buflen); + SC_TEST_GOTO_ERR(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to put the private key - key greater than 2048 bits ?"); + r = SC_SUCCESS; +err: + sc_mem_clear(buffer, sizeof(buffer)); + LOG_FUNC_RETURN(card->ctx, r); +} + +// remove a crt file +static int gids_delete_key_file(sc_card_t *card, int containernum) { + int r; + char ch_tmp[10]; + sc_path_t cpath; + snprintf(ch_tmp, sizeof(ch_tmp), "B0%02X",containernum + GIDS_FIRST_KEY_IDENTIFIER); + sc_format_path(ch_tmp, &cpath); + r = iso_ops->select_file(card, &cpath, NULL); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to select the key file"); + // delete current selected file + memset(&cpath, 0, sizeof(cpath)); + r = iso_ops->delete_file(card, &cpath); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to delete the key file"); + return r; +} + +// encode a certificate using the minidriver compression +static int gids_encode_certificate(sc_card_t *card, u8* source, size_t sourcesize, u8* destination, size_t* destinationsize) { + int r; + size_t outlen; + if (*destinationsize < 4) { + return SC_ERROR_BUFFER_TOO_SMALL; + } + if (sourcesize > 0xFFFF) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + } + // format is: + // 2 bytes for compression version + // 2 bytes for uncompressed file size + // ZLIB compression of the certificate + destination[0] = 1; + destination[1] = 0; + destination[2] = sourcesize & 0xFF; + destination[3] = (sourcesize & 0xFF00) >> 8; + outlen = *destinationsize - 4; + r = sc_compress(destination + 4, &outlen, source, sourcesize, COMPRESSION_ZLIB); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to compress the certificate"); + *destinationsize = outlen + 4; + return SC_SUCCESS; +} + +// save a certificate associated to a container to the card +static int gids_save_certificate(sc_card_t *card, sc_pkcs15_object_t *certobject, + sc_pkcs15_object_t *privkeyobject, struct sc_path *path) { + int r; + u8 certbuffer[MAX_GIDS_FILE_SIZE]; + size_t certbuffersize = sizeof(certbuffer); + struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) certobject->data; + struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *) privkeyobject->data; + unsigned char containernum; + char filename[9]; + assert((certobject->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT); + assert((privkeyobject->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY); + + // refresh the cached data in case some thing has been modified + r = gids_read_masterfile(card); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read masterfile failed"); + r= gids_read_cmapfile(card); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read cmapfile failed"); + + // compress the certificate according to the minidriver specification + r = gids_encode_certificate(card, cert_info->value.value, cert_info->value.len, certbuffer, &certbuffersize); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to encode the certificate"); + + // save it to a minidriver file + containernum = prkey_info->key_reference - GIDS_FIRST_KEY_IDENTIFIER; + if (!(prkey_info->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT)) { + snprintf(filename, sizeof(filename), "ksc%02X", containernum); + } else { + snprintf(filename, sizeof(filename), "kxc%02X", containernum); + } + + r = gids_does_file_exists(card, "mscp", filename); + if (r == SC_ERROR_FILE_NOT_FOUND) { + r = gids_create_file(card, "mscp", filename); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to create the certificate file"); + } + r = gids_write_gidsfile(card, "mscp", filename, certbuffer, certbuffersize); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to write the certificate data"); + + // return the path to the DO + r = gids_build_certificate_path(card, containernum, !(prkey_info->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT), path); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to build the certificate path"); + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +// remove a container and its registration in the cmapfile +static int gids_delete_container_num(sc_card_t *card, size_t containernum) { + int r; + u8 cmapbuffer[MAX_GIDS_FILE_SIZE]; + size_t cmapbuffersize = 0; + u8 keymapbuffer[MAX_GIDS_FILE_SIZE]; + size_t keymapbuffersize = 0; + size_t keymaprecordnum = 0; + struct gids_private_data *data = (struct gids_private_data *) card->drv_data; + size_t recordnum; + PCONTAINER_MAP_RECORD records = ((PCONTAINER_MAP_RECORD) cmapbuffer) + containernum; + struct gids_keymap_record* keymaprecord = NULL; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + // masterfile & cmapfile have been refreshed before + + recordnum = (data->cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); + + // sanity check + if (containernum >= recordnum || recordnum > GIDS_MAX_CONTAINER) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + + // refresh the key map file + keymapbuffersize = sizeof(keymapbuffer); + r = gids_get_DO(card, KEYMAP_FI, KEYMAP_DO, keymapbuffer, &keymapbuffersize); + if (r<0) { + // the keymap DO should be present if the cmapfile is not empty + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + } + keymaprecordnum = (keymapbuffersize - 1) / sizeof(struct gids_keymap_record); + if (keymaprecordnum != recordnum) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + } + + // update the key map file + memcpy(cmapbuffer, data->cmapfile, data->cmapfilesize); + cmapbuffersize = data->cmapfilesize; + keymaprecord = ((struct gids_keymap_record*)(keymapbuffer +1)) + containernum; + + memset(records, 0, sizeof(CONTAINER_MAP_RECORD)); + memset(keymaprecord, 0, sizeof(struct gids_keymap_record)); + + keymaprecord->unknownWithFFFF = (unsigned short) (-1); + keymaprecord->keyref =(unsigned short) (-1); + + // remove the key, update the key map & cmap file and signal the change + r = gids_delete_key_file(card, (int) containernum); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to delete the key file"); + r = gids_update_cardcf(card, 0, 1); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cardcf file regarding container"); + r = gids_put_DO(card, KEYMAP_FI, KEYMAP_DO, keymapbuffer, keymapbuffersize); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the keymap file"); + + r = gids_write_gidsfile(card, "mscp", "cmapfile", cmapbuffer, cmapbuffersize); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to write the cmap file after the container creation"); + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); +} + +// delete a certificate associated to a container +static int gids_delete_cert(sc_card_t *card, sc_pkcs15_object_t* object) { + int r; + struct gids_private_data *privatedata = (struct gids_private_data *) card->drv_data; + struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data; + unsigned short fileIdentifier, DO; + u8 masterfilebuffer[MAX_GIDS_FILE_SIZE]; + size_t masterfilebuffersize = 0; + gids_mf_record_t *records = (gids_mf_record_t *) masterfilebuffer; + size_t recordcount, recordnum = (size_t) -1; + size_t i; + + + assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT); + // refresh the cached data in case some thing has been modified + r = gids_read_masterfile(card); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read masterfile failed"); + r= gids_read_cmapfile(card); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read cmapfile failed"); + + // remove the file reference from the masterfile + if (cert_info->path.len != 4) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + } + fileIdentifier = cert_info->path.value[0] * 0x100 + cert_info->path.value[1]; + DO = cert_info->path.value[2] * 0x100 + cert_info->path.value[3]; + + memcpy(masterfilebuffer, privatedata->masterfile, privatedata->masterfilesize); + masterfilebuffersize = privatedata->masterfilesize; + + recordcount = (masterfilebuffersize / sizeof(gids_mf_record_t)); + for (i = 0; i < recordcount; i++) { + if (records[i].fileIdentifier == fileIdentifier && records[i].dataObjectIdentifier == DO) { + recordnum = i; + break; + } + } + if (recordnum == (size_t) -1) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND); + } + + for (i = (recordnum+1) * sizeof(gids_mf_record_t); i < masterfilebuffersize; i++) { + masterfilebuffer[i - sizeof(gids_mf_record_t)] = masterfilebuffer[i]; + } + masterfilebuffersize -= sizeof(gids_mf_record_t); + + // remove the DO, update the masterfile, and signal the change + r = gids_update_cardcf(card, 1, 0); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to update the cache file"); + + r = gids_put_DO(card, fileIdentifier, DO, NULL, 0); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to delete the certificate DO"); + + r = gids_put_DO(card, MF_FI, MF_DO, masterfilebuffer, masterfilebuffersize); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to update the masterfile"); + + memcpy(privatedata->masterfile, masterfilebuffer, masterfilebuffersize); + privatedata->masterfilesize = masterfilebuffersize; + + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +static int gids_delete_key(sc_card_t *card, sc_pkcs15_object_t* object) { + int r; + size_t containernum; + struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; + + assert((object->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_PRKEY); + // refresh the cached data in case some thing has been modified + r = gids_read_masterfile(card); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read masterfile failed"); + r = gids_read_cmapfile(card); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids read cmapfile failed"); + containernum = key_info->key_reference - GIDS_FIRST_KEY_IDENTIFIER; + + r = gids_delete_container_num(card, containernum); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to delete the container"); + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); +} + +// used by gids_initialize to create the filesystem +static int gids_initialize_create_file(sc_card_t *card, u8* command, size_t commandsize) { + int r; + sc_apdu_t apdu; + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, INS_CREATE_FILE, 0x00, 0x00); + apdu.lc = commandsize; + apdu.data = command; + apdu.datalen = commandsize; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU1 transmit failed"); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + + // activate file + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, INS_ACTIVATE_FILE, 0x00, 0x00); + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU2 transmit failed"); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); +} + +// used by gids_initialize to set the admin key +static int gids_set_administrator_key(sc_card_t *card, u8* key) { + int r; + u8 adminKeyData[] = {0x84,0x01,0x80, // key reference + 0xA5,0x1F, // key template + // key value + 0x87,0x18,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x01, + 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x01,0x02, + 0x03,0x04,0x05,0x06,0x07,0x08, + // key file + 0x88, 0x03,0xB0,0x73,0xDC}; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + memcpy(adminKeyData+7, key, 24); + r = gids_put_DO(card, GIDS_APPLET_EFID, GIDS_PUT_KEY_DO, adminKeyData, sizeof(adminKeyData)); + sc_mem_clear(adminKeyData, sizeof(adminKeyData)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to set the admin key"); + return SC_SUCCESS; +} + +static int gids_check_that_card_is_new(sc_card_t *card) { + int r; + // retrieve the masterfile + // if it succeed, the card has already been initialized + r = gids_read_masterfile(card); + if (r == SC_SUCCESS) { + r = SC_ERROR_INVALID_CARD; + LOG_TEST_RET(card->ctx, r, "unable to read the masterfile"); + } + return SC_SUCCESS; +} + +// initialize a card +// see the minidriver specification annex for the details about this +static int gids_initialize(sc_card_t *card, sc_cardctl_gids_init_param_t* param) { + sc_apdu_t apdu; + int r; +#ifdef ENABLE_OPENSSL + int i; +#endif + // hardcoded file setting + // File type=39=TLV structure for BER-TLV DOs then ACL varies depending on the file) + // this DO EF are used like DF file so the permission has to be set only once + u8 UserCreateDeleteDirAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x00,0x8C,0x03,0x03,0x30,0x00}; + u8 EveryoneReadUserWriteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x10,0x8C,0x03,0x03,0x30,0x00}; + u8 UserWriteExecuteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x11,0x8C,0x03,0x03,0x30,0xFF}; + u8 EveryoneReadAdminWriteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x12,0x8C,0x03,0x03,0x20,0x00}; + u8 UserReadWriteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x13,0x8C,0x03,0x03,0x30,0x30}; + u8 AdminReadWriteAc[] = {0x62,0x0C,0x82,0x01,0x39,0x83,0x02,0xA0,0x14,0x8C,0x03,0x03,0x20,0x20}; + // File type=18=key file ; type = symetric key + u8 AdminKey[] = {0x62,0x1A,0x82,0x01,0x18,0x83,0x02,0xB0,0x80,0x8C,0x04,0x87,0x00,0x20,0xFF,0xA5, + 0x0B,0xA4,0x09,0x80,0x01,0x02,0x83,0x01,0x80,0x95,0x01,0xC0}; + // file used to store other file references. Format undocumented. + u8 masterfile[] = {0x01,0x6d,0x73,0x63,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x63,0x61,0x72,0x64,0x69,0x64,0x00,0x00,0x00,0x00,0x00,0x20,0xdf, + 0x00,0x00,0x12,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x61, + 0x72,0x64,0x61,0x70,0x70,0x73,0x00,0x00,0x00,0x21,0xdf,0x00,0x00,0x10,0xa0,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x61,0x72,0x64,0x63,0x66,0x00,0x00, + 0x00,0x00,0x00,0x22,0xdf,0x00,0x00,0x10,0xa0,0x00,0x00,0x6d,0x73,0x63,0x70,0x00,0x00, + 0x00,0x00,0x00,0x63,0x6d,0x61,0x70,0x66,0x69,0x6c,0x65,0x00,0x00,0x00,0x23,0xdf,0x00, + 0x00,0x10,0xa0,0x00,0x00}; + // list the application on the card - defined in the minidriver specification + u8 cardapps[] = {0x6d,0x73,0x63,0x70,0x00,0x00,0x00,0x00}; + // used to detect if modifications have been done outside of the minidriver - defined in the minidriver specification + u8 cardcf[] = {0x00,0x00,0x00,0x00,0x00,0x00}; + struct sc_pin_cmd_data pindata; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + // avoid multiple initialization + r = gids_check_that_card_is_new(card); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "card seems to have been already initialized"); + + memset(&pindata, 0, sizeof(pindata)); + // create PIN & PUK + pindata.cmd = SC_PIN_CMD_CHANGE; + pindata.pin_type = SC_AC_CHV; + pindata.pin2.len = param->user_pin_len; + pindata.pin2.data = param->user_pin; + pindata.pin_reference = 0x80; + + r = sc_pin_cmd(card, &pindata, NULL); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids set pin"); + + // create file + r = gids_initialize_create_file(card, UserCreateDeleteDirAc, sizeof(UserCreateDeleteDirAc)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file UserCreateDeleteDirAc"); + r = gids_initialize_create_file(card, EveryoneReadUserWriteAc, sizeof(EveryoneReadUserWriteAc)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file EveryoneReadUserWriteAc"); + r = gids_initialize_create_file(card, UserWriteExecuteAc, sizeof(UserWriteExecuteAc)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file UserWriteExecuteAc"); + r = gids_initialize_create_file(card, EveryoneReadAdminWriteAc, sizeof(EveryoneReadAdminWriteAc)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file EveryoneReadAdminWriteAc"); + r = gids_initialize_create_file(card, UserReadWriteAc, sizeof(UserReadWriteAc)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file UserReadWriteAc"); + r = gids_initialize_create_file(card, AdminReadWriteAc, sizeof(AdminReadWriteAc)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file AdminReadWriteAc"); + + //admin key + r = gids_initialize_create_file(card, AdminKey, sizeof(AdminKey)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids to create the file AdminKey"); + + r = gids_set_administrator_key(card, param->init_code); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to set the admin key"); + + // create the filesystem + r = gids_put_DO(card, MF_FI, MF_DO, masterfile, sizeof(masterfile)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the masterfile"); + r = gids_put_DO(card, CARDAPPS_FI, CARDAPPS_DO, cardapps, sizeof(cardapps)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the cardapps"); + r = gids_put_DO(card, CARDCF_FI, CARDCF_DO, cardcf, sizeof(cardcf)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the cardcf"); + r = gids_put_DO(card, CMAP_FI, CMAP_DO, NULL, 0); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the cmapfile"); +#ifdef ENABLE_OPENSSL + for (i = sizeof(param->cardid) -1; i >= 0; i--) { + if (param->cardid[i]) break; + } + if (i < 0) { + // set a random cardid if not set + r = RAND_bytes(param->cardid, sizeof(param->cardid)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to set a random serial number"); + + } +#endif + r = gids_put_DO(card, CARDID_FI, CARDID_DO, param->cardid, sizeof(param->cardid)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "gids unable to save the cardid"); + + //select applet + sc_format_apdu(card, &apdu, SC_APDU_CASE_3, INS_SELECT, 0x00, 0x0C); + apdu.lc = 2; + apdu.data = (const unsigned char *) "\x3F\xFF"; + apdu.datalen = 2; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + // activate file + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, INS_ACTIVATE_FILE, 0x00, 0x00); + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); +} + +// execute an admin authentication based on a secret key +// this is a 3DES authentication with a secret key +// the card mechanism is described in the GIDS specification and the computer side on the minidriver specification +// the minidriver specification is incorrect because it is not ECB but CBC +// then the GIDS specification is incorrect because the z1 key should be 8 bytes instead of 7 +// this data comes from the reverse of the GIDS minidriver. +static int gids_authenticate_admin(sc_card_t *card, u8* key) { +#ifndef ENABLE_OPENSSL + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); +#else + EVP_CIPHER_CTX ctx; + int r; + u8 apduSetRandom[20] = {0x7C,0x12,0x81,0x10,0}; + u8* randomR1 = apduSetRandom + 4; + u8 apduSetRandomResponse[256]; + u8* randomR2 = apduSetRandomResponse+4; + u8 apduSendReponse[40 + 4] = {0x7C,0x2A,0x82,0x28}; + // according to the specification, the z size (z1||z2) should be 14 bytes + // but because the buffer must be a multiple of the 3DES block size (8 bytes), 7 isn't working + u8 z1[8]; + u8 buffer[16+16+8]; + u8* buffer2 = apduSendReponse + 4; + int buffer2size = 40; + u8 apduSendResponseResponse[256]; + sc_apdu_t apdu; + const EVP_CIPHER *cipher; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + // init crypto + EVP_CIPHER_CTX_init(&ctx); + // this is CBC instead of ECB + cipher = EVP_des_ede3_cbc(); + if (!cipher) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + } + + // select the admin key + sc_format_apdu(card, &apdu, SC_APDU_CASE_3, INS_MANAGE_SECURITY_ENVIRONMENT, 0xC1, 0xA4); + apdu.lc = 3; + apdu.data = (const unsigned char *) "\x83\x01\x80"; + apdu.datalen = 3; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + + // generate a challenge + r = RAND_bytes(randomR1, 16); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to set computer random"); + + // send it to the card + memcpy(apduSetRandom+4, randomR1, 16); + sc_format_apdu(card, &apdu, SC_APDU_CASE_4, INS_GENERAL_AUTHENTICATE, 0x00, 0x00); + apdu.lc = sizeof(apduSetRandom); + apdu.data = apduSetRandom; + apdu.datalen = sizeof(apduSetRandom); + apdu.resp = apduSetRandomResponse; + apdu.resplen = sizeof(apduSetRandomResponse); + apdu.le = 256; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + + // compute the half size of the mutual authentication secret + r = RAND_bytes(z1, sizeof(z1)); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to set computer random"); + + // Encrypt R2||R1||Z1 + memcpy(buffer, randomR2, 16); + memcpy(buffer+16, randomR1, 16); + memcpy(buffer+32, z1, sizeof(z1)); + if (!EVP_EncryptInit(&ctx, cipher, key, NULL)) { + EVP_CIPHER_CTX_cleanup(&ctx); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + } + EVP_CIPHER_CTX_set_padding(&ctx,0); + if (!EVP_EncryptUpdate(&ctx, buffer2, &buffer2size, buffer, sizeof(buffer))) { + EVP_CIPHER_CTX_cleanup(&ctx); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + } + + if(!EVP_EncryptFinal(&ctx, buffer2+buffer2size, &buffer2size)) { + EVP_CIPHER_CTX_cleanup(&ctx); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + } + EVP_CIPHER_CTX_cleanup(&ctx); + // send it to the card + sc_format_apdu(card, &apdu, SC_APDU_CASE_4, INS_GENERAL_AUTHENTICATE, 0x00, 0x00); + apdu.lc = sizeof(apduSendReponse); + apdu.data = apduSendReponse; + apdu.datalen = sizeof(apduSendReponse); + apdu.resp = apduSendResponseResponse; + apdu.resplen = sizeof(apduSendResponseResponse); + apdu.le = 256; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_check_sw(card, apdu.sw1, apdu.sw2), "invalid return"); + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); +#endif +} + +static int gids_card_ctl(sc_card_t * card, unsigned long cmd, void *ptr) +{ + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL); + switch (cmd) { + case SC_CARDCTL_GET_SERIALNR: + return gids_get_serialnr(card, (sc_serial_number_t *) ptr); + case SC_CARDCTL_GIDS_GET_ALL_CONTAINERS: + return gids_get_all_containers(card, (size_t*) ptr); + case SC_CARDCTL_GIDS_GET_CONTAINER_DETAIL: + return gids_get_container_detail(card, (sc_cardctl_gids_get_container_t*) ptr); + case SC_CARDCTL_GIDS_SELECT_KEY_REFERENCE: + return gids_select_key_reference(card, (sc_pkcs15_prkey_info_t*) ptr); + case SC_CARDCTL_GIDS_CREATE_KEY: + return gids_create_keyfile(card, (sc_pkcs15_object_t*) ptr); + case SC_CARDCTL_GIDS_GENERATE_KEY: + return gids_generate_key(card, ((struct sc_cardctl_gids_genkey*) ptr)->object, ((struct sc_cardctl_gids_genkey*) ptr)->pubkey); + case SC_CARDCTL_GIDS_IMPORT_KEY: + return gids_import_key(card, ((struct sc_cardctl_gids_importkey*) ptr)->object, ((struct sc_cardctl_gids_importkey*) ptr)->key); + case SC_CARDCTL_GIDS_SAVE_CERT: + return gids_save_certificate(card, ((struct sc_cardctl_gids_save_cert*) ptr)->certobject, + ((struct sc_cardctl_gids_save_cert*) ptr)->privkeyobject, ((struct sc_cardctl_gids_save_cert*) ptr)->path); + case SC_CARDCTL_GIDS_DELETE_CERT: + return gids_delete_cert(card, (sc_pkcs15_object_t*) ptr); + case SC_CARDCTL_GIDS_DELETE_KEY: + return gids_delete_key(card, (sc_pkcs15_object_t*) ptr); + case SC_CARDCTL_GIDS_INITIALIZE: + return gids_initialize(card, (sc_cardctl_gids_init_param_t*) ptr); + case SC_CARDCTL_GIDS_SET_ADMIN_KEY: + return gids_set_administrator_key(card, (u8*) ptr); + case SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN: + return gids_authenticate_admin(card, (u8*) ptr); + default: + return SC_ERROR_NOT_SUPPORTED; + } +} + +static struct sc_card_driver *sc_get_driver(void) +{ + + if (iso_ops == NULL) + iso_ops = sc_get_iso7816_driver()->ops; + + + gids_ops.match_card = gids_match_card; + gids_ops.init = gids_init; + gids_ops.finish = gids_finish; + gids_ops.read_binary = gids_read_binary; + gids_ops.write_binary = NULL; + gids_ops.update_binary = NULL; + gids_ops.erase_binary = NULL; + gids_ops.read_record = NULL; + gids_ops.write_record = NULL; + gids_ops.append_record = NULL; + gids_ops.update_record = NULL; + gids_ops.select_file = gids_select_file; + gids_ops.get_response = iso_ops->get_response; + gids_ops.get_challenge = NULL; + gids_ops.verify = NULL; // see pin_cmd + gids_ops.logout = gids_logout; + gids_ops.restore_security_env = NULL; + gids_ops.set_security_env = gids_set_security_env; + gids_ops.decipher = iso_ops->decipher; + gids_ops.compute_signature = iso_ops->compute_signature; + gids_ops.change_reference_data = NULL; // see pin_cmd + gids_ops.reset_retry_counter = NULL; // see pin_cmd + gids_ops.create_file = iso_ops->create_file; + gids_ops.delete_file = NULL; + gids_ops.list_files = NULL; + gids_ops.check_sw = iso_ops->check_sw; + gids_ops.card_ctl = gids_card_ctl; + gids_ops.process_fci = iso_ops->process_fci; + gids_ops.construct_fci = iso_ops->construct_fci; + gids_ops.pin_cmd = gids_pin_cmd; + gids_ops.get_data = NULL; + gids_ops.put_data = NULL; + gids_ops.delete_record = NULL; + gids_ops.read_public_key = gids_read_public_key; + return &gids_drv; +} + +struct sc_card_driver *sc_get_gids_driver(void) +{ + return sc_get_driver(); +} + +#else + +struct sc_card_driver *sc_get_gids_driver(void) +{ + return SC_ERROR_WRONG_CARD; +} + +#endif diff --git a/src/libopensc/card-gids.h b/src/libopensc/card-gids.h new file mode 100644 index 0000000000..03ab85a2ce --- /dev/null +++ b/src/libopensc/card-gids.h @@ -0,0 +1,117 @@ +/* + * card-gids.h: Support for GIDS smart cards. + * + * Copyright (C) 2015 Vincent Le Toux (My Smart Logon) + * + * 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 CARD_GIDS_H_ +#define CARD_GIDS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "pkcs15.h" + +struct sc_cardctl_gids_genkey { + sc_pkcs15_object_t *object; + struct sc_pkcs15_pubkey* pubkey; +}; + +struct sc_cardctl_gids_importkey { + sc_pkcs15_object_t *object; + sc_pkcs15_prkey_t *key; +}; + +struct sc_cardctl_gids_save_cert { + sc_pkcs15_object_t *certobject; + sc_pkcs15_object_t *privkeyobject; + struct sc_path *path; +}; + +typedef struct sc_cardctl_gids_get_container { + size_t containernum; + int pubusage; + int prvusage; + char label[40]; + int module_length; + sc_path_t certificatepath; +} sc_cardctl_gids_get_container_t; + +typedef struct sc_cardctl_gids_init_param { + u8 init_code[24]; + size_t user_pin_len; + u8* user_pin; + u8 cardid[16]; +} sc_cardctl_gids_init_param_t; + +// information about common files +#define UserCreateDeleteDirAc_FI 0xA000 +#define EveryoneReadUserWriteAc_FI 0xA010 +#define EveryoneReadAdminWriteAc_FI 0xA012 +#define MF_FI UserCreateDeleteDirAc_FI +#define MF_DO 0xDF1F +#define CERT_FI EveryoneReadUserWriteAc_FI +#define KEYMAP_FI UserCreateDeleteDirAc_FI +#define KEYMAP_DO 0xDF20 +#define CARDAPPS_FI EveryoneReadUserWriteAc_FI +#define CARDAPPS_DO 0xDF21 +#define CARDCF_FI EveryoneReadUserWriteAc_FI +#define CARDCF_DO 0xDF22 +#define CMAP_FI EveryoneReadUserWriteAc_FI +#define CMAP_DO 0xDF23 +#define CARDID_FI EveryoneReadAdminWriteAc_FI +#define CARDID_DO 0xDF20 +#define GIDS_MAX_DO 0xDFFF + +#define MAX_GIDS_FILE_SIZE 65000 + +typedef struct gids_mf_record { + char directory[9]; + char filename[9]; + int dataObjectIdentifier; + int fileIdentifier; +} gids_mf_record_t; + +struct gids_keymap_record { + int state; + unsigned char algid; + unsigned char keytype; + unsigned short keyref; + unsigned short unknownWithFFFF; +}; + +#define GIDS_MAX_CONTAINER 126 + +// stolen from cardmod.h +#define MAX_CONTAINER_NAME_LEN 39 +#define CONTAINER_MAP_VALID_CONTAINER 1 +#define CONTAINER_MAP_DEFAULT_CONTAINER 2 +typedef struct _CONTAINER_MAP_RECORD +{ + unsigned short wszGuid [MAX_CONTAINER_NAME_LEN + 1]; + unsigned char bFlags; + unsigned char bReserved; + unsigned short wSigKeySizeBits; + unsigned short wKeyExchangeKeySizeBits; +} CONTAINER_MAP_RECORD, *PCONTAINER_MAP_RECORD; + +#ifdef __cplusplus +} +#endif + +#endif /* CARD_GIDS_H_ */ diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index 9dbf9bca62..6f1f68dc0d 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -262,7 +262,24 @@ enum { */ SC_CARDCTL_ISOAPPLET_BASE = _CTL_PREFIX('I','S','O'), SC_CARDCTL_ISOAPPLET_GENERATE_KEY, - SC_CARDCTL_ISOAPPLET_IMPORT_KEY + SC_CARDCTL_ISOAPPLET_IMPORT_KEY, + + /* + * GIDS cards + */ + SC_CARDCTL_GIDS_BASE = _CTL_PREFIX('G','I','D'), + SC_CARDCTL_GIDS_GET_ALL_CONTAINERS, + SC_CARDCTL_GIDS_GET_CONTAINER_DETAIL, + SC_CARDCTL_GIDS_SELECT_KEY_REFERENCE, + SC_CARDCTL_GIDS_CREATE_KEY, + SC_CARDCTL_GIDS_GENERATE_KEY, + SC_CARDCTL_GIDS_IMPORT_KEY, + SC_CARDCTL_GIDS_SAVE_CERT, + SC_CARDCTL_GIDS_DELETE_KEY, + SC_CARDCTL_GIDS_DELETE_CERT, + SC_CARDCTL_GIDS_INITIALIZE, + SC_CARDCTL_GIDS_SET_ADMIN_KEY, + SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN, }; enum { diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index 652f2db985..4a813f992e 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -218,7 +218,13 @@ enum { /* Masktech cards */ SC_CARD_TYPE_MASKTECH_BASE = 29000, - SC_CARD_TYPE_MASKTECH_GENERIC + SC_CARD_TYPE_MASKTECH_GENERIC, + + /* GIDS cards */ + SC_CARD_TYPE_GIDS_BASE = 30000, + SC_CARD_TYPE_GIDS_GENERIC, + SC_CARD_TYPE_GIDS_V1, + SC_CARD_TYPE_GIDS_V2, }; extern sc_card_driver_t *sc_get_default_driver(void); @@ -257,6 +263,7 @@ extern sc_card_driver_t *sc_get_epass2003_driver(void); extern sc_card_driver_t *sc_get_dnie_driver(void); extern sc_card_driver_t *sc_get_isoApplet_driver(void); extern sc_card_driver_t *sc_get_masktech_driver(void); +extern sc_card_driver_t *sc_get_gids_driver(void); #ifdef __cplusplus } diff --git a/src/libopensc/compression.c b/src/libopensc/compression.c index 9b38d822e7..dd2ea4eefc 100644 --- a/src/libopensc/compression.c +++ b/src/libopensc/compression.c @@ -63,6 +63,31 @@ static int detect_method(const u8* in, size_t inLen) { } } +static int sc_compress_gzip(u8* out, size_t* outLen, const u8* in, size_t inLen) { + /* Since compress does not offer a way to make it compress gzip... manually set it up */ + z_stream gz; + int err; + int window_size = 15 + 0x10; + memset(&gz, 0, sizeof(gz)); + + gz.next_in = (u8*)in; + gz.avail_in = inLen; + gz.next_out = out; + gz.avail_out = *outLen; + + err = deflateInit2(&gz, Z_BEST_COMPRESSION, Z_DEFLATED, window_size, 9, Z_DEFAULT_STRATEGY); + if(err != Z_OK) return zerr_to_opensc(err); + err = deflate(&gz, Z_FINISH); + if(err != Z_STREAM_END) { + deflateEnd(&gz); + return zerr_to_opensc(err); + } + *outLen = gz.total_out; + + err = deflateEnd(&gz); + return zerr_to_opensc(err); +} + static int sc_decompress_gzip(u8* out, size_t* outLen, const u8* in, size_t inLen) { /* Since uncompress does not offer a way to make it uncompress gzip... manually set it up */ z_stream gz; @@ -88,6 +113,23 @@ static int sc_decompress_gzip(u8* out, size_t* outLen, const u8* in, size_t inLe return zerr_to_opensc(err); } +int sc_compress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method) { + unsigned long zlib_outlen; + int rc; + + switch(method) { + case COMPRESSION_ZLIB: + zlib_outlen = *outLen; + rc = zerr_to_opensc(compress(out, &zlib_outlen, in, inLen)); + *outLen = zlib_outlen; + return rc; + case COMPRESSION_GZIP: + return sc_compress_gzip(out, outLen, in, inLen); + default: + return SC_ERROR_INVALID_ARGUMENTS; + } +} + int sc_decompress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method) { unsigned long zlib_outlen; int rc; diff --git a/src/libopensc/compression.h b/src/libopensc/compression.h index c84a565692..c27b2d55ab 100644 --- a/src/libopensc/compression.h +++ b/src/libopensc/compression.h @@ -28,6 +28,8 @@ #define COMPRESSION_GZIP 2 #define COMPRESSION_UNKNOWN (-1) +int sc_compress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method); + int sc_decompress_alloc(u8** out, size_t* outLen, const u8* in, size_t inLen, int method); int sc_decompress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method); diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index dbcc99c893..95dbf89a9b 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -112,6 +112,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = { { "PIV-II", (void *(*)(void)) sc_get_piv_driver }, { "itacns", (void *(*)(void)) sc_get_itacns_driver }, { "isoApplet", (void *(*)(void)) sc_get_isoApplet_driver }, + { "gids", (void *(*)(void)) sc_get_gids_driver }, { "openpgp", (void *(*)(void)) sc_get_openpgp_driver }, /* The default driver should be last, as it handles all the * unrecognized cards. */ diff --git a/src/libopensc/libopensc.exports b/src/libopensc/libopensc.exports index e7f725bb8b..d2a19eb948 100644 --- a/src/libopensc/libopensc.exports +++ b/src/libopensc/libopensc.exports @@ -280,6 +280,7 @@ sc_pkcs15init_get_cardos_ops sc_pkcs15init_get_cryptoflex_ops sc_pkcs15init_get_cyberflex_ops sc_pkcs15init_get_gpk_ops +sc_pkcs15init_get_gids_ops sc_pkcs15init_get_incrypto34_ops sc_pkcs15init_get_jcop_ops sc_pkcs15init_get_manufacturer diff --git a/src/libopensc/pkcs15-gids.c b/src/libopensc/pkcs15-gids.c new file mode 100644 index 0000000000..ef6fea2e6f --- /dev/null +++ b/src/libopensc/pkcs15-gids.c @@ -0,0 +1,244 @@ +/* + * pkcs15-gids.c: Support for GIDS smart cards. + * + * Copyright (C) 2015 Vincent Le Toux (My Smart Logon) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "internal.h" +#include "pkcs15.h" +#include "common/compat_strlcpy.h" +#include "cardctl.h" + +#ifdef ENABLE_ZLIB + +#include "card-gids.h" + +/* + * Add a key from a minidriver container + */ +static int sc_pkcs15emu_gids_add_prkey(sc_pkcs15_card_t * p15card, sc_cardctl_gids_get_container_t *container) { + + sc_card_t *card = p15card->card; + sc_pkcs15_prkey_info_t prkey_info; + sc_pkcs15_object_t prkey_obj; + sc_pkcs15_pubkey_info_t pubkey_info; + sc_pkcs15_object_t pubkey_obj; + sc_pkcs15_cert_info_t cert_info; + sc_pkcs15_object_t cert_obj; + int r; + char ch_tmp[10]; + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "Got args: containerIndex=%x\n", container->containernum); + + memset(&prkey_info, 0, sizeof(prkey_info)); + memset(&prkey_obj, 0, sizeof(prkey_obj)); + + prkey_info.id.len = 1; + prkey_info.id.value[0] = container->containernum; + prkey_info.modulus_length = container->module_length; + prkey_info.usage = container->prvusage; + prkey_info.native = 1; + prkey_info.key_reference = 0x81 + container->containernum; + + strlcpy(prkey_obj.label, container->label, sizeof(prkey_obj.label)); + prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; + prkey_obj.auth_id.len = 1; + prkey_obj.auth_id.value[0] = 0x80; + + r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to sc_pkcs15emu_add_rsa_prkey"); + + memset(&pubkey_info, 0, sizeof(pubkey_info)); + memset(&pubkey_obj, 0, sizeof(pubkey_obj)); + + strlcpy(pubkey_obj.label, container->label, sizeof(pubkey_obj.label)); + + snprintf(ch_tmp, sizeof(ch_tmp), "3FFFB0%02X", prkey_info.key_reference); + sc_format_path(ch_tmp, &pubkey_info.path); + pubkey_info.native = 1; + pubkey_info.key_reference = prkey_info.key_reference; + pubkey_info.modulus_length = prkey_info.modulus_length; + pubkey_info.usage = container->pubusage; + pubkey_info.id = prkey_info.id; + + r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to sc_pkcs15emu_add_rsa_pubkey"); + + if (container->certificatepath.len > 0) { + memset(&cert_info, 0, sizeof(cert_info)); + memset(&cert_obj, 0, sizeof(cert_obj)); + + cert_info.id = prkey_info.id; + cert_info.path.count = -1; + cert_info.path = container->certificatepath; + + strlcpy(cert_obj.label, container->label, sizeof(cert_obj.label)); + r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); + LOG_TEST_RET(card->ctx, r, "Could not add certificate"); + } else { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No certificate found"); + } + + return SC_SUCCESS; +} + +/* + * Initialize PKCS#15 emulation with user PIN, private keys, certificate and data objects + * + */ +static int sc_pkcs15emu_gids_init (sc_pkcs15_card_t * p15card) +{ + sc_card_t *card = p15card->card; + int r; + size_t i; + struct sc_pkcs15_auth_info pin_info; + struct sc_pkcs15_object pin_obj; + struct sc_pin_cmd_data pin_cmd_data; + size_t recordsnum; + + r = sc_card_ctl(card, SC_CARDCTL_GIDS_GET_ALL_CONTAINERS, &recordsnum); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the containers. Uninitialized card ?"); + + r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, NULL); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to get the serial number. Uninitialized card ?"); + + p15card->tokeninfo->serial_number = (char*) malloc(card->serialnr.len *2 +1); + if (!p15card->tokeninfo->serial_number) { + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + sc_bin_to_hex(card->serialnr.value, card->serialnr.len, p15card->tokeninfo->serial_number, card->serialnr.len *2 +1, 0); + + if (p15card->tokeninfo->label == NULL) { + p15card->tokeninfo->label = strdup("GIDS card"); + if (p15card->tokeninfo->label == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + + if ((p15card->tokeninfo->manufacturer_id != NULL) && !strcmp("(unknown)", p15card->tokeninfo->manufacturer_id)) { + free(p15card->tokeninfo->manufacturer_id); + p15card->tokeninfo->manufacturer_id = NULL; + } + + if (p15card->tokeninfo->manufacturer_id == NULL) { + p15card->tokeninfo->manufacturer_id = strdup("www.mysmartlogon.com"); + if (p15card->tokeninfo->manufacturer_id == NULL) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + } + if (p15card->card->type == SC_CARD_TYPE_GIDS_V2) { + p15card->tokeninfo->version = 2; + } else if (p15card->card->type == SC_CARD_TYPE_GIDS_V1) { + p15card->tokeninfo->version = 1; + } + + 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] = 0x80; + pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; + pin_info.attrs.pin.reference = 0x80; + pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_LOCAL|SC_PKCS15_PIN_FLAG_INITIALIZED; + 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 = 15; + pin_info.attrs.pin.pad_char = '\0'; + pin_info.tries_left = -1; + pin_info.max_tries = -1; + + memset(&pin_cmd_data, 0, sizeof(pin_cmd_data)); + pin_cmd_data.cmd = SC_PIN_CMD_GET_INFO; + pin_cmd_data.pin_type = SC_AC_CHV; + pin_cmd_data.pin_reference = pin_info.attrs.pin.reference; + + r = sc_pin_cmd(card, &pin_cmd_data, NULL); + if (r == SC_SUCCESS) { + pin_info.max_tries = pin_cmd_data.pin1.max_tries; + pin_info.tries_left = pin_cmd_data.pin1.tries_left; + } + + strlcpy(pin_obj.label, "UserPIN", sizeof(pin_obj.label)); + pin_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE|SC_PKCS15_CO_FLAG_MODIFIABLE; + + r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to sc_pkcs15emu_add_pin_obj"); + + // add the PUK if it is available on the card. Not all card have a PUK + pin_info.attrs.pin.reference = 0x81; + pin_info.auth_id.value[0] = 0x81; + pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_LOCAL|SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN; + pin_info.attrs.pin.reference = 0x81; + pin_cmd_data.pin_reference = pin_info.attrs.pin.reference; + + r = sc_pin_cmd(card, &pin_cmd_data, NULL); + if (r == SC_SUCCESS) { + pin_info.max_tries = pin_cmd_data.pin1.max_tries; + pin_info.tries_left = pin_cmd_data.pin1.tries_left; + strlcpy(pin_obj.label, "PUK", sizeof(pin_obj.label)); + r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to sc_pkcs15emu_add_pin_obj with PUK"); + } + + r = sc_card_ctl(card, SC_CARDCTL_GIDS_GET_ALL_CONTAINERS, &recordsnum); + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "sc_card_ctl SC_CARDCTL_GIDS_GET_ALL_CONTAINERS"); + + for (i = 0; i < recordsnum; i++) { + sc_cardctl_gids_get_container_t container; + memset(&container, 0, sizeof(sc_cardctl_gids_get_container_t)); + container.containernum = i; + r = sc_card_ctl(card, SC_CARDCTL_GIDS_GET_CONTAINER_DETAIL, &container); + if (r < 0) { + // one of the container information couldn't be retrieved + // ignore it + continue; + } + sc_pkcs15emu_gids_add_prkey(p15card, &container); + } + return SC_SUCCESS; +} + +int sc_pkcs15emu_gids_init_ex(sc_pkcs15_card_t *p15card, + sc_pkcs15emu_opt_t *opts) +{ + if (opts && (opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)) { + return sc_pkcs15emu_gids_init(p15card); + } else { + if (p15card->card->type != SC_CARD_TYPE_GIDS_GENERIC && p15card->card->type != SC_CARD_TYPE_GIDS_V1 && p15card->card->type != SC_CARD_TYPE_GIDS_V2) { + return SC_ERROR_WRONG_CARD; + } + return sc_pkcs15emu_gids_init(p15card); + } +} + +#else + +int sc_pkcs15emu_gids_init_ex(sc_pkcs15_card_t *p15card, + sc_pkcs15emu_opt_t *opts) +{ + return SC_ERROR_WRONG_CARD; +} + +#endif diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c index b886366f67..0ccb944daa 100644 --- a/src/libopensc/pkcs15-syn.c +++ b/src/libopensc/pkcs15-syn.c @@ -71,6 +71,8 @@ extern int sc_pkcs15emu_sc_hsm_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); extern int sc_pkcs15emu_dnie_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); +extern int sc_pkcs15emu_gids_init_ex(sc_pkcs15_card_t *, + sc_pkcs15emu_opt_t *); static struct { const char * name; @@ -95,6 +97,7 @@ static struct { { "oberthur", sc_pkcs15emu_oberthur_init_ex }, { "sc-hsm", sc_pkcs15emu_sc_hsm_init_ex }, { "dnie", sc_pkcs15emu_dnie_init_ex }, + { "gids", sc_pkcs15emu_gids_init_ex }, { NULL, NULL } }; diff --git a/src/pkcs15init/Makefile.am b/src/pkcs15init/Makefile.am index bf267df82e..7d9d5f7214 100644 --- a/src/pkcs15init/Makefile.am +++ b/src/pkcs15init/Makefile.am @@ -30,7 +30,7 @@ dist_pkgdata_DATA = \ ias_adele_admin1.profile ias_adele_admin2.profile ias_adele_common.profile \ iasecc_generic_pki.profile iasecc_admin_eid.profile iasecc_generic_oberthur.profile \ openpgp.profile sc-hsm.profile \ - isoApplet.profile + isoApplet.profile gids.profile AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\" \ -I$(top_srcdir)/src @@ -48,4 +48,4 @@ libpkcs15init_la_SOURCES = \ pkcs15-oberthur.c pkcs15-oberthur-awp.c \ pkcs15-authentic.c pkcs15-iasecc.c pkcs15-openpgp.c \ pkcs15-sc-hsm.c \ - pkcs15-isoApplet.c + pkcs15-isoApplet.c pkcs15-gids.c diff --git a/src/pkcs15init/Makefile.mak b/src/pkcs15init/Makefile.mak index fb4b28fbd1..ac5aac50bc 100644 --- a/src/pkcs15init/Makefile.mak +++ b/src/pkcs15init/Makefile.mak @@ -10,7 +10,7 @@ OBJECTS = pkcs15-lib.obj profile.obj \ pkcs15-entersafe.obj pkcs15-rtecp.obj pkcs15-westcos.obj \ pkcs15-myeid.obj pkcs15-authentic.obj pkcs15-iasecc.obj \ pkcs15-epass2003.obj pkcs15-openpgp.obj pkcs15-sc-hsm.obj \ - pkcs15-isoApplet.obj + pkcs15-isoApplet.obj pkcs15-gids.obj all: $(TARGET) diff --git a/src/pkcs15init/gids.profile b/src/pkcs15init/gids.profile new file mode 100644 index 0000000000..176f672f6d --- /dev/null +++ b/src/pkcs15init/gids.profile @@ -0,0 +1,11 @@ +# +# PKCS15 r/w profile for GIDS cards +# +cardinfo { + label = "GIDS"; + manufacturer = "MYSMARTLOGON"; + + max-pin-length = 16; + min-pin-length = 4; + pin-encoding = ascii-numeric; +} diff --git a/src/pkcs15init/pkcs15-gids.c b/src/pkcs15init/pkcs15-gids.c new file mode 100644 index 0000000000..db013c375c --- /dev/null +++ b/src/pkcs15init/pkcs15-gids.c @@ -0,0 +1,192 @@ +/* + * pkcs15-gids.c: Support for GIDS smart cards. + * + * Copyright (C) 2015 Vincent Le Toux (My Smart Logon) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "../libopensc/log.h" +#include "../libopensc/opensc.h" +#include "../libopensc/cardctl.h" +#include "../libopensc/asn1.h" +#include "pkcs15-init.h" +#include "profile.h" + +#include "../libopensc/card-gids.h" + +/* + * Select a key reference. + */ +static int +gids_select_key_reference(sc_profile_t *profile, sc_pkcs15_card_t *p15card, + sc_pkcs15_prkey_info_t *key_info) +{ + sc_card_t *card = p15card->card; + LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_SELECT_KEY_REFERENCE, key_info)); +} + +/* + * Create a new key file. + */ +static int +gids_create_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *obj) +{ + sc_card_t *card = p15card->card; + LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_CREATE_KEY, obj)); +} + + +/* + * Generate a new key. + */ +static int +gids_generate_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, + sc_pkcs15_object_t *obj, + sc_pkcs15_pubkey_t *pubkey) +{ + sc_card_t *card = p15card->card; + struct sc_cardctl_gids_genkey call = {obj, pubkey}; + LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_GENERATE_KEY, &call)); +} + +/* + * Store a usable private key on the card. + */ +static int +gids_store_key(sc_profile_t *profile, sc_pkcs15_card_t *p15card, sc_pkcs15_object_t *object, + sc_pkcs15_prkey_t *key) +{ + sc_card_t *card = p15card->card; + + struct sc_cardctl_gids_importkey call = {object, key}; + LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_IMPORT_KEY, &call)); +} + +static int +gids_delete_object(struct sc_profile *profile, struct sc_pkcs15_card * p15card, + struct sc_pkcs15_object *object, const struct sc_path *path) { + sc_card_t *card = p15card->card; + switch(object->type & SC_PKCS15_TYPE_CLASS_MASK) { + case SC_PKCS15_TYPE_PRKEY: + LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_DELETE_KEY, object)); + break; + case SC_PKCS15_TYPE_CERT: + LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_DELETE_CERT, object)); + break; + case SC_PKCS15_TYPE_PUBKEY: + LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); + default: + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } +} + +static int gids_emu_update_any_df(struct sc_profile *profile, struct sc_pkcs15_card *p15card, + unsigned op, struct sc_pkcs15_object *object) +{ + LOG_FUNC_CALLED(p15card->card->ctx); + /* After storing object, pkcs15init will call this function to update DF. + * But GIDS has no other DF than GIDS-Application, so we do nothing. */ + LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); +} + +static int gids_save_certificate(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object, struct sc_path *path) { + int r; + sc_card_t *card = p15card->card; + struct sc_cardctl_gids_save_cert call = {object, NULL, path}; + struct sc_pkcs15_cert_info *cert_info = (struct sc_pkcs15_cert_info *) object->data; + + r = sc_pkcs15_find_object_by_id(p15card, SC_PKCS15_TYPE_PRKEY, &cert_info->id , &(call.privkeyobject)); + if (r == SC_ERROR_OBJECT_NOT_FOUND) { + //TODO save the certificate in the special file + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); + } + SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "unable to find the private key associated to the certificate"); + + LOG_FUNC_RETURN(card->ctx, sc_card_ctl(card, SC_CARDCTL_GIDS_SAVE_CERT, &call)); +} + +static int gids_emu_store_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile, + struct sc_pkcs15_object *object, struct sc_pkcs15_der *content, + struct sc_path *path) { + sc_card_t *card = p15card->card; + int r; + + LOG_FUNC_CALLED(card->ctx); + + switch (object->type & SC_PKCS15_TYPE_CLASS_MASK) { + case SC_PKCS15_TYPE_PRKEY: + case SC_PKCS15_TYPE_PUBKEY: + /* For these two type, store_data just don't need to do anything. + * All have been done already before this function is called */ + r = SC_SUCCESS; + break; + case SC_PKCS15_TYPE_CERT: + r = gids_save_certificate(p15card, object, path); + break; + default: + r = SC_ERROR_NOT_IMPLEMENTED; + break; + } + LOG_FUNC_RETURN(card->ctx, r); +} + +static int gids_emu_update_tokeninfo(sc_profile_t *profile, sc_pkcs15_card_t *p15card, + sc_pkcs15_tokeninfo_t *tokeninfo) +{ + LOG_FUNC_CALLED(p15card->card->ctx); + /* When unbinding pkcs15init, this function will be called. + * But for GIDS, token info does not need to change, we do nothing. */ + LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); +} + +static struct sc_pkcs15init_operations sc_pkcs15init_gids_operations = +{ + NULL, /* erase_card */ + NULL, /* init_card */ + NULL, /* create_dir */ + NULL, /* create_domain */ + NULL, /* pin_reference*/ + NULL, /* create_pin */ + gids_select_key_reference, /* key_reference */ + gids_create_key, /* create_key */ + gids_store_key, /* store_key */ + gids_generate_key, /* generate_key */ + NULL, NULL, /* encode private/public key */ + NULL, /* finalize */ + gids_delete_object, /* delete_object */ + NULL, /* pkcs15init emulation emu_update_dir */ + gids_emu_update_any_df, /* pkcs15init emulation emu_update_any_df */ + gids_emu_update_tokeninfo, /* pkcs15init emulation emu_update_tokeninfo */ + NULL, /* pkcs15init emulation emu_write_info */ + gids_emu_store_data, /* pkcs15init emulation emu_store_data */ + NULL, /* sanity_check*/ +}; + +struct +sc_pkcs15init_operations *sc_pkcs15init_get_gids_ops(void) +{ + return &sc_pkcs15init_gids_operations; +} diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index 8ab0af7138..441fc547f0 100644 --- a/src/pkcs15init/pkcs15-init.h +++ b/src/pkcs15init/pkcs15-init.h @@ -423,6 +423,7 @@ extern struct sc_pkcs15init_operations *sc_pkcs15init_get_piv_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_openpgp_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_sc_hsm_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_isoApplet_ops(void); +extern struct sc_pkcs15init_operations *sc_pkcs15init_get_gids_ops(void); #ifdef __cplusplus } diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c index a4f338a58b..58c6e8388c 100644 --- a/src/pkcs15init/pkcs15-lib.c +++ b/src/pkcs15init/pkcs15-lib.c @@ -155,6 +155,7 @@ static struct profile_operations { { "myeid", (void *) sc_pkcs15init_get_myeid_ops }, { "sc-hsm", (void *) sc_pkcs15init_get_sc_hsm_ops }, { "isoApplet", (void *) sc_pkcs15init_get_isoApplet_ops }, + { "gids", (void *) sc_pkcs15init_get_gids_ops }, #ifdef ENABLE_OPENSSL { "authentic", (void *) sc_pkcs15init_get_authentic_ops }, { "iasecc", (void *) sc_pkcs15init_get_iasecc_ops }, diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index 3061496e79..5fb7f5e545 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -8,7 +8,7 @@ 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 sc-hsm-tool dnie-tool + westcos-tool sc-hsm-tool dnie-tool gids-tool endif # compile with $(PTHREAD_CFLAGS) to allow debugging with gdb @@ -51,6 +51,8 @@ sc_hsm_tool_SOURCES = sc-hsm-tool.c util.c sc_hsm_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) dnie_tool_SOURCES = dnie-tool.c util.c dnie_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) +gids_tool_SOURCES = gids-tool.c util.c +gids_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) if WIN32 opensc_tool_SOURCES += versioninfo-tools.rc @@ -68,4 +70,5 @@ westcos_tool_SOURCES += versioninfo-tools.rc openpgp_tool_SOURCES += versioninfo-tools.rc iasecc_tool_SOURCES += versioninfo-tools.rc sc_hsm_tool_SOURCES += versioninfo-tools.rc +gids_tool_SOURCES += versioninfo-tools.rc endif diff --git a/src/tools/gids-tool.c b/src/tools/gids-tool.c new file mode 100644 index 0000000000..43ef7d450b --- /dev/null +++ b/src/tools/gids-tool.c @@ -0,0 +1,584 @@ +/* + * gids-tool.c: Support for GIDS smart cards. + * + * Copyright (C) 2015 Vincent Le Toux (My Smart Logon) + * + * 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 + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include + +#include +#include + +#include "libopensc/opensc.h" +#include "libopensc/cardctl.h" +#include "libopensc/card-gids.h" +#include "libopensc/asn1.h" +#include "util.h" + +static const char *app_name = "gids-tool"; + +static struct sc_aid gids_aid = { { 0xA0,0x00,0x00,0x03,0x97,0x42,0x54,0x46,0x59 }, 9 }; + +static int opt_wait = 0; +static char *opt_reader = NULL; +static int verbose = 0; + +enum { + OPT_SO_PIN = 0x100, + OPT_PIN, + OPT_SERIAL_NUMBER, + OPT_NEW_KEY, +}; + +static const struct option options[] = { + { "initialize", 0, NULL, 'X' }, + { "admin-key", 1, NULL, OPT_SO_PIN }, + { "pin", 1, NULL, OPT_PIN }, + { "serial-number", 1, NULL, OPT_SERIAL_NUMBER }, + { "unblock", 0, NULL, 'U' }, + { "change-admin-key", 0, NULL, 'C' }, + { "new-admin-key", 1, NULL, OPT_NEW_KEY }, + { "reader", 1, NULL, 'r' }, + { "wait", 0, NULL, 'w' }, + { "verbose", 0, NULL, 'v' }, + { NULL, 0, NULL, 0 } +}; + +static const char *option_help[] = { + "Initialize token", + "Define the administrator key", + "Define user PIN", + "Define serial number", + "Unblock the user PIN after an administrator authentication", + "Change the administrator key", + "Define the new adminastrator key", + "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 int initialize(sc_card_t *card, const char *so_pin, const char *user_pin, const char* serial) +{ + sc_cardctl_gids_init_param_t param; + size_t len; + char *_so_pin = NULL, *_user_pin = NULL, *_serial = NULL; + int r; + + memset(¶m, 0, sizeof(sc_cardctl_gids_init_param_t)); + + if (so_pin == NULL) { + printf("Enter admin key (48 hexadecimal characters) : \n"); + printf("Press Enter to set the admin key to 00...00\n"); + util_getpass(&_so_pin, NULL, stdin); + printf("\n"); + } else { + _so_pin = (char *)so_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 -1; + } + + if (len == 0) { + } else if (len != 24) { + fprintf(stderr, "The admin key must be a hexadecimal string of 48 characters\n"); + return -1; + } + + if (user_pin == NULL) { + printf("Enter initial User-PIN (4 - 16 characters) : "); + util_getpass(&_user_pin, NULL, stdin); + printf("\n"); + } else { + _user_pin = (char *)user_pin; + } + + if (serial == NULL) { + printf("Enter serial number (32 hexadecimal characters): \n"); + printf("Press Enter to set a random serial number\n"); + util_getpass(&_serial, NULL, stdin); + printf("\n"); + } else { + _serial = (char *)serial; + } + len = sizeof(param.cardid); + r = sc_hex_to_bin(_serial, param.cardid, &len); + if (r < 0) { + fprintf(stderr, "Error decoding serial number (%s)\n", sc_strerror(r)); + return -1; + } + if (len == 0) { + memset(param.cardid, 0, sizeof(param.cardid)); + } else if (len != 32) { + fprintf(stderr, "the serial number must be a hexadecimal string of 32 characters\n"); + return -1; + } + + param.user_pin_len = strlen(_user_pin); + + if (param.user_pin_len < 4) { + fprintf(stderr, "PIN must be at least 4 characters long\n"); + return -1; + } + + if (param.user_pin_len > 16) { + fprintf(stderr, "PIN must not be longer than 16 characters\n"); + return -1; + } + + param.user_pin = (u8 *)_user_pin; + + r = sc_card_ctl(card, SC_CARDCTL_GIDS_INITIALIZE, (void *)¶m); + if (r < 0) { + fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GIDS_INITIALIZE, *) failed with %s\n", sc_strerror(r)); + } + + return 0; +} + +static int unblock(sc_card_t* card, const char *so_pin, const char *user_pin) { + int r; + char *_so_pin = NULL, *_user_pin = NULL; + size_t len; + u8 key[24]; + struct sc_pin_cmd_data data; + + memset(&data, 0, sizeof(struct sc_pin_cmd_data)); + + if (so_pin == NULL) { + printf("============================================================\n"); + printf("WARNING\n"); + printf("Entering an incorrect admin key can break your card\n"); + printf("WARNING\n"); + printf("============================================================\n"); + printf("Enter admin key (48 hexadecimal characters) : "); + util_getpass(&_so_pin, NULL, stdin); + printf("\n"); + } else { + _so_pin = (char *)so_pin; + } + + len = sizeof(key); + r = sc_hex_to_bin(_so_pin, key, &len); + if (r < 0) { + fprintf(stderr, "Error decoding initialization code (%s)\n", sc_strerror(r)); + return -1; + } + + if (len != 24) { + fprintf(stderr, "admin key must be a hexadecimal string of 48 characters\n"); + return -1; + } + + r = sc_card_ctl(card, SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN, (void *)key); + if (r < 0) { + fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN, *) failed with %s\n", sc_strerror(r)); + return -1; + } + printf("Administrator authentication successful\n"); + printf("Setting the new PIN\n"); + + if (user_pin == NULL) { + printf("Enter User-PIN (4 - 16 characters) : "); + util_getpass(&_user_pin, NULL, stdin); + printf("\n"); + } else { + _user_pin = (char *)user_pin; + } + + data.pin_type = SC_AC_CHV; + data.cmd = SC_PIN_CMD_UNBLOCK; + data.pin2.len = strlen(_user_pin); + data.pin2.data = (unsigned char*) _user_pin; + data.pin_reference = 0x80; + r = sc_pin_cmd(card, &data, NULL); + if (r < 0) { + fprintf(stderr, "reset pin failed with %s\n", sc_strerror(r)); + return -1; + } + printf("Unblock PIN done successfuly\n"); + // the card should have deauthenticated the admin, but to be sure: + sc_logout(card); + return 0; +} + +static int changeAdminKey(sc_card_t* card, const char *so_pin, const char* new_key) { + char *_so_pin = NULL, *_new_key = NULL; + size_t len; + u8 key[24]; + int r; + + if (so_pin == NULL) { + printf("============================================================\n"); + printf("WARNING\n"); + printf("Entering an incorrect admin key can break your card\n"); + printf("WARNING\n"); + printf("============================================================\n"); + printf("Enter admin key (48 hexadecimal characters) : "); + util_getpass(&_so_pin, NULL, stdin); + printf("\n"); + } else { + _so_pin = (char *)so_pin; + } + + len = sizeof(key); + r = sc_hex_to_bin(_so_pin, key, &len); + if (r < 0) { + fprintf(stderr, "Error decoding initialization code (%s)\n", sc_strerror(r)); + return -1; + } + + if (len != 24) { + fprintf(stderr, "admin key must be a hexadecimal string of 48 characters\n"); + return -1; + } + + r = sc_card_ctl(card, SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN, (void *)key); + if (r < 0) { + fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN, *) failed with %s\n", sc_strerror(r)); + return -1; + } + + if (new_key == NULL) { + printf("Enter new admin key (48 hexadecimal characters) : "); + util_getpass(&_so_pin, NULL, stdin); + printf("\n"); + } else { + _new_key = (char *)new_key; + } + + len = sizeof(key); + r = sc_hex_to_bin(_new_key, key, &len); + if (r < 0) { + fprintf(stderr, "Error decoding initialization code (%s)\n", sc_strerror(r)); + return -1; + } + + if (len != 24) { + fprintf(stderr, "admin key must be a hexadecimal string of 48 characters\n"); + return -1; + } + + r = sc_card_ctl(card, SC_CARDCTL_GIDS_SET_ADMIN_KEY, (void *)key); + sc_logout(card); + if (r < 0) { + fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GIDS_SET_ADMIN_KEY, *) failed with %s\n", sc_strerror(r)); + return -1; + } + return 0; +} + +// read a DO from the card +static int gids_get_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdentifier, u8* response, size_t *responselen) { + sc_apdu_t apdu; + int r; + u8 data[4] = {0x5C, 0x02, (dataObjectIdentifier&0xFF00)>>8, (dataObjectIdentifier&0xFF)}; + size_t datasize = 0; + const u8* p; + u8 buffer[MAX_GIDS_FILE_SIZE]; + + sc_format_apdu(card, &apdu, + response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, 0xCB, (fileIdentifier&0xFF00)>>8, (fileIdentifier&0xFF)); + apdu.lc = 04; + apdu.data = data; + apdu.datalen = 04; + apdu.resp = buffer; + apdu.resplen = sizeof(buffer); + apdu.le = 256; + + r = sc_transmit_apdu(card, &apdu); + if (r < 0) + return r; + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r < 0) + return r; + + p = sc_asn1_find_tag(card->ctx, buffer, sizeof(buffer), dataObjectIdentifier, &datasize); + if (!p) { + return SC_ERROR_FILE_NOT_FOUND; + } + if (datasize > *responselen) { + return SC_ERROR_BUFFER_TOO_SMALL; + } + memcpy(response, p, datasize); + *responselen = datasize; + return SC_SUCCESS; +} + +static int print_info(sc_card_t *card) { + int r; + u8 buffer[MAX_GIDS_FILE_SIZE]; + size_t size = sizeof(buffer); + u8 masterfile[MAX_GIDS_FILE_SIZE]; + size_t masterfilesize = sizeof(masterfile); + u8 cmapfile[MAX_GIDS_FILE_SIZE]; + size_t cmapfilesize = sizeof(cmapfile); + u8 keymap[MAX_GIDS_FILE_SIZE]; + size_t keymapsize = sizeof(keymap); + gids_mf_record_t *records = (gids_mf_record_t *) (masterfile+1); + int recordcount; + int i; + + printf("===============================\n"); + printf("Dumping the content of the card\n"); + printf("===============================\n"); + r = gids_get_DO(card, MF_FI, MF_DO, masterfile, &masterfilesize); + if (r < 0) { + fprintf(stderr, "unable to retrieve the master file: %s\n", sc_strerror(r)); + fprintf(stderr, "Is that a new card ?\n"); + return r; + } + printf("Dumping Files:\n"); + if (masterfilesize >= 1) { + recordcount = (int) ((masterfilesize-1) / sizeof(gids_mf_record_t)); + printf("Found %d entries in the masterfile\n", recordcount); + for (i = 0; i < recordcount; i++) { + if (records[i].filename[0] == 0) { + printf(" Directory: %s\n", records[i].directory); + printf(" FileIdentifier: 0x%x\n", records[i].fileIdentifier); + printf("\n"); + } + } + for (i = 0; i < recordcount; i++) { + if (records[i].filename[0] != 0) { + printf(" File: %s\\%s\n", records[i].directory, records[i].filename); + printf(" FileIdentifier: 0x%x\n", records[i].fileIdentifier); + printf(" DataObjectIdentifier: 0x%x\n", records[i].dataObjectIdentifier); + size = sizeof(buffer); + r = gids_get_DO(card, records[i].fileIdentifier, records[i].dataObjectIdentifier, buffer, &size); + if (r < 0) { + printf(" unable to read the file: %s\n", sc_strerror(r)); + } else { +#ifdef _WIN32 + // visual studio doesn't support %zu + printf(" Size: %Iu\n", size); +#else + printf(" Size: %zu\n", size); +#endif + } + printf("\n"); + if (strcmp(records[i].directory, "mscp") == 0 && strcmp(records[i].filename, "cmapfile") == 0 ) { + cmapfilesize = size; + memcpy(cmapfile, buffer, size); + } + } + } + printf("Dumping containers:\n"); + if (cmapfilesize == sizeof(cmapfile)) { + printf("Unable to find the container file (mscp\\cmapfile)\n"); + } else { + PCONTAINER_MAP_RECORD cmaprecords = (PCONTAINER_MAP_RECORD) cmapfile; + int cmaprecordnum = (cmapfilesize / sizeof(CONTAINER_MAP_RECORD)); + int keymaprecordnum = -1; + struct gids_keymap_record* keymaprecord = ((struct gids_keymap_record*)(keymap +1)); + if (cmaprecordnum == 0) { + printf(" no container found\n"); + } else { + r = gids_get_DO(card, KEYMAP_FI, KEYMAP_DO, keymap, &keymapsize); + if (r < 0) { + printf(" the keymap couldn't be found\n"); + } else { + keymaprecordnum = (keymapsize - 1) / sizeof(struct gids_keymap_record); + } + for (i = 0; i < cmaprecordnum; i++) { + printf(" container: %d\n", i); + wprintf(L" guid: %s\n", cmaprecords[i].wszGuid); + printf(" bFlags: "); + if (cmaprecords[i].bFlags & CONTAINER_MAP_VALID_CONTAINER) { + printf("Valid container"); + if (cmaprecords[i].bFlags & CONTAINER_MAP_DEFAULT_CONTAINER) { + printf(",Default container"); + } + } else { + printf("Empty container"); + } + printf("\n"); + printf(" wSigKeySizeBits: %d\n", cmaprecords[i].wSigKeySizeBits); + printf(" wKeyExchangeKeySizeBits: %d\n", cmaprecords[i].wKeyExchangeKeySizeBits); + if (i < keymaprecordnum) { + printf(" key info:\n"); + printf(" state: %d\n", keymaprecord[i].state); + printf(" algid: %d\n", keymaprecord[i].algid); + printf(" keyref: 0x%x\n", keymaprecord[i].keyref); + printf(" key type: "); + switch(keymaprecord[i].keytype) { + case 0: + printf("none\n"); + break; + case 0x9C: + printf("signature\n"); + break; + case 0x9A: + printf("signature + decryption\n"); + break; + default: + printf("unknown\n"); + break; + } + } + printf("\n"); + } + } + } + } else { + printf("No file system found\n"); + } + return SC_SUCCESS; +} + +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_unblock = 0; + int do_change_admin = 0; + sc_path_t path; + const char *opt_so_pin = NULL; + const char *opt_pin = NULL; + const char *opt_serial_number = NULL; + const char *opt_new_key = NULL; + sc_context_param_t ctx_param; + + setbuf(stderr, NULL); + setbuf(stdout, NULL); + + while (1) { + c = getopt_long(argc, argv, "XUCr: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 OPT_SO_PIN: + util_get_pin(optarg, &opt_so_pin); + break; + case OPT_PIN: + util_get_pin(optarg, &opt_pin); + break; + case OPT_SERIAL_NUMBER: + util_get_pin(optarg, &opt_serial_number); + break; + case OPT_NEW_KEY: + util_get_pin(optarg, &opt_new_key); + break; + case 'U': + do_unblock = 1; + action_count++; + break; + case 'C': + do_change_admin = 1; + action_count++; + 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)); + exit(1); + } + + /* Only change if not in opensc.conf */ + if (verbose > 1 && ctx->debug == 0) { + ctx->debug = verbose; + sc_ctx_log_to_file(ctx, "stderr"); + } + + r = util_connect_card(ctx, &card, opt_reader, opt_wait, verbose); + if (r != SC_SUCCESS) { + if (r < 0) { + fprintf(stderr, "Failed to connect to card: %s\n", sc_strerror(err)); + } + goto end; + } + + sc_path_set(&path, SC_FILE_TYPE_WORKING_EF, gids_aid.value, gids_aid.len, 0, 0); + r = sc_select_file(card, &path, NULL); + + if (r != SC_SUCCESS) { + fprintf(stderr, "Failed to select application: %s\n", sc_strerror(r)); + goto fail; + } + + if (do_initialize && initialize(card, opt_so_pin, opt_pin, opt_serial_number)) + goto fail; + + if (do_unblock && unblock(card, opt_so_pin, opt_pin)) + goto fail; + + if (do_change_admin && changeAdminKey(card, opt_so_pin, opt_new_key)) + goto fail; + + if (action_count == 0) { + print_info(card); + } + + err = 0; + goto end; +fail: + err = 1; +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/Make.rules.mak b/win32/Make.rules.mak index 33fc91b6c9..ec1e20ceb9 100644 --- a/win32/Make.rules.mak +++ b/win32/Make.rules.mak @@ -50,7 +50,7 @@ OPENSSL_LIB = $(OPENSSL_DIR)\lib\VC\$(OPENSSL_STATIC_DIR)\libeay32MT.lib user32. !ENDIF PROGRAMS_OPENSSL = cryptoflex-tool.exe pkcs15-init.exe netkey-tool.exe piv-tool.exe \ - westcos-tool.exe sc-hsm-tool.exe dnie-tool.exe + westcos-tool.exe sc-hsm-tool.exe dnie-tool.exe gids-tool.exe OPENSC_FEATURES = $(OPENSC_FEATURES) openssl CANDLEFLAGS = -dOpenSSL="$(OPENSSL_DIR)" $(CANDLEFLAGS) !ENDIF diff --git a/win32/OpenSC.wxs.in b/win32/OpenSC.wxs.in index ae0a8d1693..c96783ff99 100644 --- a/win32/OpenSC.wxs.in +++ b/win32/OpenSC.wxs.in @@ -176,6 +176,9 @@ + + + @@ -270,6 +273,9 @@ + + + @@ -327,6 +333,7 @@ + @@ -357,6 +364,7 @@ +