etc/opensc.conf.in | 2 +- src/libopensc/Makefile.am | 3 +- src/libopensc/Makefile.in | 9 +- src/libopensc/card-rutoken.c | 1455 ++++++++++++++++++++++++++++++++++ src/libopensc/cardctl.h | 135 +++- src/libopensc/ctx.c | 1 + src/libopensc/opensc.h | 7 + src/libopensc/pkcs15-algo.c | 6 + src/libopensc/pkcs15-prkey-rutoken.c | 364 +++++++++ src/libopensc/pkcs15-rutoken.c | 216 +++++ src/libopensc/pkcs15-syn.c | 3 + src/pkcs11/framework-pkcs15.c | 22 +- src/pkcs11/pkcs11.h | 5 + src/pkcs15init/Makefile.am | 5 +- src/pkcs15init/Makefile.in | 8 +- src/pkcs15init/pkcs15-init.h | 1 + src/pkcs15init/pkcs15-lib.c | 1 + src/pkcs15init/pkcs15-rutoken.c | 698 ++++++++++++++++ src/pkcs15init/rutoken.profile | 89 ++ src/tools/Makefile.am | 5 +- src/tools/Makefile.in | 15 +- src/tools/pkcs11-tool.c | 1 + src/tools/rutoken-tool.c | 537 +++++++++++++ 23 files changed, 3571 insertions(+), 17 deletions(-) diff --git a/etc/opensc.conf.in b/etc/opensc.conf.in index a4d112d..05a43c6 100644 --- a/etc/opensc.conf.in +++ b/etc/opensc.conf.in @@ -279,7 +279,7 @@ app default { # Default: yes # enable_builtin_emulation = yes; # list of the builtin pkcs15 emulators to test - builtin_emulators = esteid, openpgp, tcos, starcert, infocamere, postecert, actalis, atrust-acos, gemsafe, tccardos, PIV-II; + builtin_emulators = esteid, openpgp, tcos, starcert, infocamere, postecert, actalis, atrust-acos, gemsafe, tccardos, PIV-II, rutoken; # additional settings per driver # diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 24863e6..354c344 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -30,11 +30,12 @@ libopensc_la_SOURCES = \ card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \ card-oberthur.c card-belpic.c card-atrust-acos.c \ card-incrypto34.c card-piv.c card-muscle.c card-acos5.c \ - card-asepcos.c card-akis.c\ + card-asepcos.c card-akis.c card-rutoken.c \ \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafe.c \ pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \ + pkcs15-rutoken.c pkcs15-prkey-rutoken.c \ compression.c p15card-helper.c libopensc_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ libopensc_la_LIBADD = @LIBSCCONF@ $(OPENSSL_LIBS) $(OPENCT_LIBS) $(PCSC_LIBS) $(LTLIB_LIBS) diff --git a/src/libopensc/Makefile.in b/src/libopensc/Makefile.in index 0db5a30..a674c2e 100644 --- a/src/libopensc/Makefile.in +++ b/src/libopensc/Makefile.in @@ -85,7 +85,8 @@ am_libopensc_la_OBJECTS = sc.lo ctx.lo ui.lo log.lo errors.lo asn1.lo \ pkcs15-tcos.lo pkcs15-esteid.lo pkcs15-postecert.lo \ pkcs15-gemsafe.lo pkcs15-actalis.lo pkcs15-atrust-acos.lo \ pkcs15-tccardos.lo pkcs15-piv.lo compression.lo \ - p15card-helper.lo + p15card-helper.lo \ + card-rutoken.lo pkcs15-rutoken.lo pkcs15-prkey-rutoken.lo libopensc_la_OBJECTS = $(am_libopensc_la_OBJECTS) binSCRIPT_INSTALL = $(INSTALL_SCRIPT) SCRIPTS = $(bin_SCRIPTS) @@ -283,7 +284,8 @@ libopensc_la_SOURCES = \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafe.c \ pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \ - compression.c p15card-helper.c + compression.c p15card-helper.c \ + card-rutoken.c pkcs15-rutoken.c pkcs15-prkey-rutoken.c libopensc_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ libopensc_la_LIBADD = @LIBSCCONF@ $(OPENSSL_LIBS) $(OPENCT_LIBS) $(PCSC_LIBS) $(LTLIB_LIBS) @@ -456,6 +458,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sec.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-rutoken.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-rutoken.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-prkey-rutoken.Plo@am__quote@ .c.o: @am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ diff --git a/src/libopensc/card-rutoken.c b/src/libopensc/card-rutoken.c new file mode 100644 index 0000000..157dbd2 --- /dev/null +++ b/src/libopensc/card-rutoken.c @@ -0,0 +1,1455 @@ +/* + * card-rutoken.c: Support for ruToken cards + * + * Copyright (C) 2007 Pavel Mironchik + * Copyright (C) 2007 Eugene Hermann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include "internal.h" +#include "cardctl.h" +#include +#include +#include +#include +#include "pkcs15.h" + +#define BIG_ENDIAN_RUTOKEN + +#ifdef HAVE_OPENSSL +#include +#include +#include +#include +#include +#include +#endif + +#define FDESCR_DF 0x38 /*00111000b*/ +#define FDESCR_EF 0x01 + +#define ID_RESERVED_CURDF 0x3FFF /*Reserved ID for current DF*/ + +#ifdef HAVE_OPENSSL +int get_prkey_from_bin(const u8 *data, size_t len, struct sc_pkcs15_prkey **key); +#endif + +#ifdef BIG_ENDIAN_RUTOKEN +#define MF_PATH "\x3F\x00" +#else +#define MF_PATH "\x00\x3F" +#endif + +struct auth_senv { + unsigned int algorithm; +#if 0 + int key_file_id; + size_t key_size; + unsigned int algorithm_flags; + sc_path_t path; +#endif +}; +typedef struct auth_senv auth_senv_t; + +static const sc_SecAttrV2_t default_sec_attr = { + 0x42, + 0, 1, 0, 0, 0, 0, 1, + 0, 2, 0, 0, 0, 0, 2 +}; + +static const struct sc_card_operations *iso_ops = NULL; + +static struct sc_card_operations rutoken_ops; + +static struct sc_card_driver rutoken_drv = { + "ruToken driver", + "rutoken", + &rutoken_ops, + NULL, 0, NULL +}; + +static struct sc_atr_table rutoken_atrs[] = { + { "3b:6f:00:ff:00:56:72:75:54:6f:6b:6e:73:30:20:00:00:90:00", NULL, NULL, SC_CARD_TYPE_GENERIC_BASE, 0, NULL }, + { NULL, NULL, NULL, 0, 0, NULL } +}; + +const char *hexdump(const void *data, size_t len) +{ + static char string[1024]; + unsigned char *d = (unsigned char *)data; + unsigned int i, left; + + string[0] = '\0'; + left = sizeof(string); + for (i = 0; len--; i += 3) { + if (i >= sizeof(string) - 4) + break; + snprintf(string + i, 4, " %02x", *d++); + } + return string; +} + +static int rutoken_finish(sc_card_t *card) +{ + SC_FUNC_CALLED(card->ctx, 1); + free(card->drv_data); + return 0; +} + +static int rutoken_match_card(sc_card_t *card) +{ + int i; + + SC_FUNC_CALLED(card->ctx, 1); + + i = _sc_match_atr(card, rutoken_atrs, &card->type); + if (i < 0) + return 0; + + sc_debug(card->ctx, "atr recognized as ruToken\n"); + return 1; +} + +static int rutoken_init(sc_card_t *card) +{ + int ret = SC_ERROR_MEMORY_FAILURE; + + SC_FUNC_CALLED(card->ctx, 1); + + card->name = "rutoken card"; + card->drv_data = malloc(sizeof(auth_senv_t)); + card->caps |= SC_CARD_CAP_RSA_2048 | SC_CARD_CAP_NO_FCI | SC_CARD_CAP_RNG; + if (card->drv_data) + { + memset(card->drv_data, 0, sizeof(auth_senv_t)); + ret = SC_NO_ERROR; + } + /* add algorithm + TODO: may nid som other flag */ + unsigned int flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1; + /* SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASH_SHA1 + | SC_ALGORITHM_RSA_HASH_MD5_SHA1 + | SC_ALGORITHM_RSA_PAD_NONE */ + + _sc_card_add_rsa_alg(card, 256, flags, 0); + _sc_card_add_rsa_alg(card, 512, flags, 0); + _sc_card_add_rsa_alg(card, 768, flags, 0); + _sc_card_add_rsa_alg(card, 1024, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); + sc_algorithm_info_t info; + flags = SC_ALGORITHM_GOST_CRYPT_PZ | SC_ALGORITHM_GOST_CRYPT_GAMM + | SC_ALGORITHM_GOST_CRYPT_GAMMOS; + memset(&info, 0, sizeof(info)); + info.algorithm = SC_ALGORITHM_GOST; + info.flags = flags; + info.key_length = 32; + if (_sc_card_add_algorithm(card, &info) < 0) + return -1; + return ret; +} + +static const struct sc_card_error rutoken_errors[] = { + + { 0x6300, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63C1, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63C2, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63C3, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63C4, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63C5, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63C6, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63C7, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63C8, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63C9, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63Ca, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63Cb, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63Cc, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63Cd, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63Ce, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + { 0x63Cf, SC_ERROR_PIN_CODE_INCORRECT,"authentication failed"}, + + { 0x6400, SC_ERROR_CARD_CMD_FAILED,"Aborting"}, + + { 0x6500, SC_ERROR_MEMORY_FAILURE, "Memory failure"}, + { 0x6581, SC_ERROR_MEMORY_FAILURE, "Memory failure"}, + + { 0x6700, SC_ERROR_WRONG_LENGTH, "Lc or Le invalid"}, + + { 0x6883, SC_ERROR_CARD_CMD_FAILED, "The finishing command of a chain is expected"}, + + { 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"required access right not granted"}, + { 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "bs object blocked"}, + { 0x6985, SC_ERROR_CARD_CMD_FAILED, "command not allowed (unsuitable conditions)"}, + { 0x6986, SC_ERROR_INCORRECT_PARAMETERS,"no current ef selected"}, + + { 0x6a80, SC_ERROR_INCORRECT_PARAMETERS,"invalid parameters in data field"}, + { 0x6a81, SC_ERROR_NOT_SUPPORTED, "function/mode not supported"}, + { 0x6a82, SC_ERROR_FILE_NOT_FOUND, "file (DO) not found"}, + { 0x6a84, SC_ERROR_CARD_CMD_FAILED, "not enough memory"}, + { 0x6a86, SC_ERROR_INCORRECT_PARAMETERS,"p1/p2 invalid"}, + { 0x6a89, SC_ERROR_FILE_ALREADY_EXISTS,"file (DO) already exists"}, + + { 0x6b00, SC_ERROR_INCORRECT_PARAMETERS,"Out of file length"}, + + { 0x6c00, SC_ERROR_WRONG_LENGTH, "le does not fit the data to be sent"}, + + { 0x6d00, SC_ERROR_INS_NOT_SUPPORTED, "ins invalid (not supported)"}, + + /* Own class of an error*/ + { 0x6f01, SC_ERROR_CARD_CMD_FAILED, "ruToken has the exchange protocol which is not supported by the USB-driver (newer, than in the driver)"}, + { 0x6f83, SC_ERROR_CARD_CMD_FAILED, "Infringement of the exchange protocol with ruToken is revealed"}, + { 0x6f84, SC_ERROR_CARD_CMD_FAILED, "ruToken is busy by processing of other command"}, + { 0x6f85, SC_ERROR_CARD_CMD_FAILED, "In the current folder the maximum quantity of file system objects is already created."}, + { 0x6f86, SC_ERROR_CARD_CMD_FAILED, "The token works not with access rights 'Visitor'"}, + + { 0x9000, SC_NO_ERROR, NULL} +}; + +int rutoken_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) +{ + const int err_count = sizeof(rutoken_errors)/sizeof(rutoken_errors[0]); + int i; + + sc_debug(card->ctx, "sw1 = %x, sw2 = %x", sw1, sw2); + + for (i = 0; i < err_count; i++) { + if (rutoken_errors[i].SWs == ((sw1 << 8) | sw2)) { + if ( rutoken_errors[i].errorstr ) + sc_debug(card->ctx, rutoken_errors[i].errorstr); + /*SC_FUNC_RETURN(card->ctx, 1, rutoken_errors[i].errorno);*/ + return rutoken_errors[i].errorno; + } + } + + sc_error(card->ctx, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2); + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_CARD_CMD_FAILED); +} + +static int rutoken_dir_up(sc_card_t *card) +{ + u8 rbuf[256]; + int r = 0; + sc_apdu_t apdu; + SC_FUNC_CALLED(card->ctx, 1); + /*sc_debug(card->ctx, "\n\tpath = %s\n\ttype = %d", hexdump(path, pathlen), in_path->type); + prepare & transmit APDU + 00 a4 00 04 20 - first*/ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x03, 0x00); + apdu.cla = 0x00; + apdu.resplen = 256; + apdu.resp = rbuf; + apdu.le = 256; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + sc_debug(card->ctx, "rbuf = %s len %d", hexdump(apdu.resp, apdu.resplen), apdu.resplen); + sc_debug(card->ctx, "sw1 = %x, sw2 = %x", apdu.sw1, apdu.sw2); + return 0; +} + +/* make little endian path from normal path. + return 1 if right len, otherwise 0 */ +static int make_le_path(u8 *hPath, size_t len) +{ +#ifdef BIG_ENDIAN_RUTOKEN + /* we don't need it any more */ + return 1; +#else + int i, ret = (len > 1) && !(len & 1); /* && (len <= SC_MAX_PATH_SIZE); */ + if (ret) + { + for(i = 0; i < len; i += 2) + { + u8 b = hPath[i]; + hPath[i] = hPath[i+1]; + hPath[i+1] = b; + } + } + return ret; +#endif +} + +static int rutoken_list_files(sc_card_t *card, u8 *buf, size_t buflen) +{ + SC_FUNC_CALLED(card->ctx, 1); + u8 rbuf[256]; + u8 previd[2]; + int r = 0, len=0; + sc_apdu_t apdu; + SC_FUNC_CALLED(card->ctx, 1); + /* sc_debug(card->ctx, "\n\tpath = %s\n\ttype = %d", hexdump(path, pathlen), in_path->type); */ + /* prepare & transmit APDU */ + /* 00 a4 00 04 20 - first */ + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x00, 0x00); + apdu.cla = 0x00; + apdu.resplen = 256; + apdu.resp = rbuf; + apdu.le = 256; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + sc_debug(card->ctx, "rbuf = %s len %d", hexdump(apdu.resp, apdu.resplen), apdu.resplen); + sc_debug(card->ctx, "sw1 = %x, sw2 = %x", apdu.sw1, apdu.sw2); + if((apdu.sw1 == 0x6a) ) + { + /* empty dir */ + return 0; + } + /* todo: add check buflen */ + /* save first file(dir) ID */ + memcpy(buf+len, rbuf+6, 2); + memcpy(previd, rbuf+6, 2); + len += 2; + if(rbuf[4] == FDESCR_DF) + rutoken_dir_up(card); + + /* 00 a4 00 02 02 prev id - next */ + while(1) + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x06); + apdu.cla = 0x00; + apdu.lc = 2; + apdu.data = previd; + apdu.datalen = 2; + apdu.resplen = 256; + apdu.resp = rbuf; + apdu.le = 256; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + sc_debug(card->ctx, "rbuf = %s len %d", hexdump(apdu.resp, apdu.resplen), apdu.resplen); + sc_debug(card->ctx, "sw1 = %x, sw2 = %x", apdu.sw1, apdu.sw2); + if((apdu.sw1 == 0x6a) ) + { + /* end list */ + break; + } + /* todo: add check buflen */ + /* save first file(dir) ID */ + memcpy(buf+len, rbuf+6, 2); + memcpy(previd, rbuf+6, 2); + len += 2; + if(rbuf[4] == FDESCR_DF) + rutoken_dir_up(card); + } + make_le_path(buf, len); + return len; +} + +static void rutoken_process_fcp(sc_card_t *card, u8 *pIn, sc_file_t *file) +{ +#ifdef BIG_ENDIAN_RUTOKEN + file->size = pIn[3] + ((u_int16_t)pIn[2])*256; + file->id = pIn[7] + ((u_int16_t)pIn[6])*256; +#else + file->size = pIn[2] + ((u_int16_t)pIn[3])*256; + file->id = pIn[6] + ((u_int16_t)pIn[7])*256; +#endif + + if (pIn[4] == FDESCR_DF) + { + file->type = SC_FILE_TYPE_DF; + } + else + { + file->type = SC_FILE_TYPE_WORKING_EF; + file->ef_structure = SC_FILE_EF_TRANSPARENT; + } + sc_file_set_sec_attr(file, pIn + 17, SEC_ATTR_SIZE); +} + +static int rutoken_select_file(sc_card_t *card, + const sc_path_t *in_path, + sc_file_t **file) +{ + int ret = SC_ERROR_INVALID_ARGUMENTS; + u8 pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; + int pathlen = in_path->len; + u8 buf[SC_MAX_APDU_BUFFER_SIZE]; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + memcpy(path, in_path->value, pathlen); + + sc_debug(card->ctx, "\n\tpath = %s\n\ttype = %d", hexdump(path, pathlen), in_path->type); + /* prepare & transmit APDU */ + if (make_le_path(path, pathlen)) + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0); + + switch (in_path->type) + { + case SC_PATH_TYPE_FILE_ID: + if (pathlen == 2) /* select file in current df */ + { + apdu.p1 = 2; + ret = SC_SUCCESS; + } + break; + case SC_PATH_TYPE_PATH: + apdu.p1 = 8; + if (memcmp(path, MF_PATH, 2) == 0) + { + if (pathlen == 2) /* select MF */ + { + apdu.p1 = 0; + } + else /* select DF */ + { + path += 2; + pathlen -= 2; + } + } + ret = SC_SUCCESS; + break; + default: + ret = SC_ERROR_NOT_SUPPORTED; + break; + } + } + if (ret == SC_SUCCESS) + { + ret = SC_ERROR_CARD_CMD_FAILED; + apdu.lc = pathlen; + apdu.data = path; + apdu.datalen = pathlen; + + if (file != NULL) { + apdu.resp = buf; + apdu.resplen = sizeof(buf); + apdu.le = 256; + } else { + apdu.resplen = 0; + apdu.le = 0; + apdu.cse = SC_APDU_CASE_3_SHORT; + } + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + + sc_debug(card->ctx, "file = %x", file); + if (file == NULL) + { + /* We don't need file info */ + if (apdu.sw1 == 0x61) + SC_FUNC_RETURN(card->ctx, 2, SC_NO_ERROR); + SC_FUNC_RETURN(card->ctx, 2, ret); + } + } + /* New file structure */ + if ((ret == SC_SUCCESS) && (apdu.resplen == 32)) + { + /* sc_file_t *tmp_ */ + *file = sc_file_new(); + if (*file == NULL) + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + (*file)->path = *in_path; /* what about selecting EF by ID (SC_PATH_TYPE_FILE_ID)? */ + + rutoken_process_fcp(card, buf, *file); + /* *file = tmp_file; */ + + sc_debug(card->ctx, + "nfile ID = %04X, path = %s, type = %02X, len = %d", + (*file)->id, hexdump((*file)->path.value, (*file)->path.len), + (*file)->type, (*file)->size); + sc_debug(card->ctx, "sec attr = %s", + hexdump((*file)->sec_attr, (*file)->sec_attr_len)); + } + SC_FUNC_RETURN(card->ctx, 2, ret); +} + +/* +static int rutoken_set_file_attributes(sc_card_t *card, sc_file_t *file) +{ + int ret = SC_ERROR_NOT_SUPPORTED; + return ret; +} +*/ + +static int rutoken_construct_fcp(sc_card_t *card, const sc_file_t *file, u8 *out) +{ + SC_FUNC_CALLED(card->ctx, 1); + + if ((!file) || (file->id == ID_RESERVED_CURDF)) + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); + memset(out, 0, 32); + switch (file->type) + { + case SC_FILE_TYPE_DF: + out[4] = 0x38; +#ifdef BIG_ENDIAN_RUTOKEN + out[0] = file->size / 256; + out[1] = file->size % 256; +#else + out[1] = file->size / 256; + out[0] = file->size % 256; +#endif + break; + case SC_FILE_TYPE_WORKING_EF: + out[4] = 0x01; + /* set the length (write to wBodyLen) */ +#ifdef BIG_ENDIAN_RUTOKEN + out[2] = file->size / 256; + out[3] = file->size % 256; +#else + out[3] = file->size / 256; + out[2] = file->size % 256; +#endif + break; + case SC_FILE_TYPE_INTERNAL_EF: + default: + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NOT_SUPPORTED); + } + /* set file ID */ +#ifdef BIG_ENDIAN_RUTOKEN + out[6] = file->id / 256; + out[7] = file->id % 256; +#else + out[7] = file->id / 256; + out[6] = file->id % 256; +#endif + /* set sec_attr */ + if(file->sec_attr_len == SEC_ATTR_SIZE) + memcpy(out + 17, file->sec_attr, SEC_ATTR_SIZE); + else + memcpy(out + 17, &default_sec_attr, SEC_ATTR_SIZE); + + SC_FUNC_RETURN(card->ctx, 1, SC_NO_ERROR); +} + +static int rutoken_create_file(sc_card_t *card, sc_file_t *file) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + sc_apdu_t apdu; + u8 sbuf[32]; + SC_FUNC_CALLED(card->ctx, 1); + memset(sbuf, 0, 32); + if((ret = rutoken_construct_fcp(card, file, sbuf)) == SC_NO_ERROR) + { + sc_debug(card->ctx, "fcp = %s", hexdump(sbuf, 32)); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); + apdu.data = sbuf; + apdu.datalen = 32; + apdu.lc = 32; + + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_delete_file(sc_card_t *card, const sc_path_t *path) +{ + u8 sbuf[2]; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + if (!path || path->type != SC_PATH_TYPE_FILE_ID || (path->len != 0 && path->len != 2)) + { + sc_error(card->ctx, "File type has to be SC_PATH_TYPE_FILE_ID\n"); + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); + } + if (path->len == 2) + { +#ifdef BIG_ENDIAN_RUTOKEN + sbuf[0] = path->value[0]; + sbuf[1] = path->value[1]; +#else + sbuf[0] = path->value[1]; + sbuf[1] = path->value[0]; +#endif + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00); + apdu.lc = 2; + apdu.datalen = 2; + apdu.data = sbuf; + } + else /* No file ID given: means currently selected file */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, 0x00, 0x00); + + SC_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed"); + SC_FUNC_RETURN(card->ctx, 1, rutoken_check_sw(card, apdu.sw1, apdu.sw2)); +} + +static int rutoken_verify(sc_card_t *card, unsigned int type, int ref_qualifier, + const u8 *data, size_t data_len, int *tries_left) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0x00, ref_qualifier); + apdu.lc = data_len; + apdu.datalen = data_len; + apdu.data = data; + if(sc_transmit_apdu(card, &apdu) >= 0) + { + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + if(ret == SC_ERROR_PIN_CODE_INCORRECT && tries_left) + *tries_left = (int)(apdu.sw2 & 0x0f); + } + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_logout(sc_card_t *card) +{ + sc_apdu_t apdu; + int ret = SC_ERROR_CARD_CMD_FAILED; + sc_path_t path; + + SC_FUNC_CALLED(card->ctx, 1); + sc_format_path("3F00", &path); + if (rutoken_select_file(card, &path, NULL) == SC_SUCCESS) + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0x00, 0x00); + apdu.cla = 0x80; + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_change_reference_data(sc_card_t *card, unsigned int type, + int ref_qualifier, const u8 *old, size_t oldlen, + const u8 *newref, size_t newlen, int *tries_left) +{ + int left; + int ret = SC_ERROR_CARD_CMD_FAILED; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + if(old && oldlen) + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, ref_qualifier); + if(sc_transmit_apdu(card, &apdu) >= 0 + && apdu.sw1 != 0x90 && apdu.sw2 != 0x00) + { + rutoken_logout(card); + rutoken_verify(card, type, ref_qualifier, old, oldlen, &left); + } + } + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier); + apdu.lc = newlen; + apdu.datalen = newlen; + apdu.data = newref; + if(sc_transmit_apdu(card, &apdu) >= 0) + { + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + if(ret == SC_ERROR_PIN_CODE_INCORRECT && tries_left) + *tries_left = (int)(apdu.sw2 & 0x0f); + } + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_reset_retry_counter(sc_card_t *card, unsigned int type, + int ref_qualifier, const u8 *puk, size_t puklen, + const u8 *newref, size_t newlen) +{ +#if 0 + int left; +#endif + int ret = SC_ERROR_CARD_CMD_FAILED; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); +#if 0 + if(puk && puklen) + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, ref_qualifier); + if(sc_transmit_apdu(card, &apdu) >= 0 + && apdu.sw1 != 0x90 && apdu.sw2 != 0x00) + { + rutoken_logout(card); + rutoken_verify(card, type, ref_qualifier, puk, puklen, &left); + } + } +#endif + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2c, 0x03, ref_qualifier); + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_restore_security_env(sc_card_t *card, int se_num) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 3, se_num); + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_set_security_env(sc_card_t *card, + const sc_security_env_t *env, + int se_num) +{ + SC_FUNC_CALLED(card->ctx, 1); + sc_apdu_t apdu; + auth_senv_t *senv = (auth_senv_t*)card->drv_data; + if (!senv || !env) return SC_ERROR_INVALID_ARGUMENTS; + u8 data[3] = {0x83, 0x01, env->key_ref[0]}; + int ret = SC_NO_ERROR; + if(env->algorithm == SC_ALGORITHM_RSA) + { + senv->algorithm = SC_ALGORITHM_RSA_RAW; + return ret; + } + else + senv->algorithm = SC_ALGORITHM_GOST; + + if (env->key_ref_len != 1) + { + sc_error(card->ctx, "No or invalid key reference\n"); + ret = SC_ERROR_INVALID_ARGUMENTS; + } + else + /* select component */ + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 1, 0); + apdu.lc = apdu.datalen = 3; + apdu.data = data; + switch (env->operation) + { + case SC_SEC_OPERATION_AUTHENTICATE: + { + apdu.p2 = 0xA4; + } + break; + case SC_SEC_OPERATION_DECIPHER: + { + apdu.p2 = 0xB8; + } + break; + case SC_SEC_OPERATION_SIGN: + { + apdu.p2 = 0xAA; + } + break; + default: + ret = SC_ERROR_INVALID_ARGUMENTS; + } + } + /* set SE */ + if (ret == SC_NO_ERROR) + { + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + } + /* set driver data */ +#if 0 + if (ret == SC_NO_ERROR) + { + /* TODO: add check */ + senv->algorithm = SC_ALGORITHM_GOST; + senv->algorithm_flags = env->algorithm_flags; + senv->key_file_id = env->key_ref[0]; + senv->key_size = 256; + } +#endif + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static void rutoken_set_do_hdr(u8 *data, sc_DOHdrV2_t *pHdr) +{ + if(data) + { +#ifdef BIG_ENDIAN_RUTOKEN + data[0] = (u8)(pHdr->wDOBodyLen / 0x100); + data[1] = (u8)(pHdr->wDOBodyLen % 0x100); +#else + data[0] = (u8)(pHdr->wDOBodyLen % 0x100); + data[1] = (u8)(pHdr->wDOBodyLen / 0x100); +#endif + data[2] = (u8)(pHdr->OTID.byObjectType); + data[3] = (u8)(pHdr->OTID.byObjectID); + data[4] = (u8)(pHdr->OP.byObjectOptions); + data[5] = (u8)(pHdr->OP.byObjectFlags); + data[6] = (u8)(pHdr->OP.byObjectTry); + memcpy(data + 7, pHdr->dwReserv1, 4); + memcpy(data + 11, pHdr->abyReserv2, 6); + memcpy(data + 17, pHdr->SA_V2, SEC_ATTR_SIZE); + } +} + +static int rutoken_key_gen(sc_card_t *card, sc_DOHdrV2_t *pHdr) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + u8 data[SC_RUTOKEN_DO_HDR_LEN]; + sc_apdu_t apdu; + if ( + (pHdr->wDOBodyLen != SC_RUTOKEN_DEF_LEN_DO_GOST) || + (pHdr->OTID.byObjectType != SC_RUTOKEN_TYPE_KEY) || + (pHdr->OP.byObjectFlags & SC_RUTOKEN_FLAGS_COMPACT_DO) || + (pHdr->OP.byObjectFlags & SC_RUTOKEN_FLAGS_FULL_OPEN_DO) || + (pHdr->OTID.byObjectID < SC_RUTOKEN_DO_ALL_MIN_ID) || + (pHdr->OTID.byObjectID > SC_RUTOKEN_DO_NOCHV_MAX_ID_V2) + ) + { + ret = SC_ERROR_INVALID_ARGUMENTS; + } + else + { + pHdr->OP.byObjectTry = 0; + rutoken_set_do_hdr(data, pHdr); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x65); + apdu.data= data; + apdu.datalen = apdu.lc = sizeof(data); + + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_create_do(sc_card_t *card, sc_DO_V2_t * pDO) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + u8 data[SC_RUTOKEN_DO_HDR_LEN + SC_RUTOKEN_DO_PART_BODY_LEN]; + sc_apdu_t apdu; + if ( + ((pDO->HDR.OTID.byObjectType & SC_RUTOKEN_TYPE_CHV) && + (pDO->HDR.OTID.byObjectID != SC_RUTOKEN_DEF_ID_GCHV_USER) && + (pDO->HDR.OTID.byObjectID != SC_RUTOKEN_DEF_ID_GCHV_ADMIN)) || + ((pDO->HDR.OTID.byObjectType == SC_RUTOKEN_ALLTYPE_GOST) && + (pDO->HDR.wDOBodyLen != SC_RUTOKEN_DEF_LEN_DO_GOST)) || + ((pDO->HDR.OTID.byObjectType == SC_RUTOKEN_ALLTYPE_SE) && + (pDO->HDR.wDOBodyLen != SC_RUTOKEN_DEF_LEN_DO_SE)) || + (pDO->HDR.OTID.byObjectID < SC_RUTOKEN_DO_ALL_MIN_ID) || + (pDO->HDR.OTID.byObjectID > SC_RUTOKEN_DO_NOCHV_MAX_ID_V2) || + ((pDO->HDR.OP.byObjectFlags & SC_RUTOKEN_FLAGS_COMPACT_DO) && + (pDO->HDR.wDOBodyLen > SC_RUTOKEN_COMPACT_DO_MAX_LEN)) || + (pDO->HDR.wDOBodyLen > SC_RUTOKEN_DO_PART_BODY_LEN) + ) + { + ret = SC_ERROR_INVALID_ARGUMENTS; + } + else + { + rutoken_set_do_hdr(data, &pDO->HDR); + memcpy(data + SC_RUTOKEN_DO_HDR_LEN, pDO->abyDOBody, pDO->HDR.wDOBodyLen); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x62); + apdu.data= data; + apdu.datalen = apdu.lc = SC_RUTOKEN_DO_HDR_LEN + pDO->HDR.wDOBodyLen; + + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_get_do_info(sc_card_t *card, sc_DO_INFO_t * pInfo) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + u8 data[1] = {pInfo->DoId}; + sc_apdu_t apdu; + + if ((pInfo->SelType != select_first) && + ((pInfo->DoId < SC_RUTOKEN_DO_ALL_MIN_ID) || + (pInfo->DoId > SC_RUTOKEN_DO_NOCHV_MAX_ID_V2))) + { + ret = SC_ERROR_INVALID_ARGUMENTS; + } + else + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x30, 0x00, 0x00); + apdu.cla = 0x80; + apdu.resp = pInfo->pDoData; + apdu.resplen = sizeof(pInfo->pDoData); + apdu.le = 255; + memset(apdu.resp, 0, apdu.resplen); + + switch(pInfo->SelType) + { + case select_first: + apdu.cse = SC_APDU_CASE_2_SHORT; + break; + case select_by_id: + apdu.data = data; + apdu.datalen = apdu.lc = 1; + break; + case select_next: + apdu.p2 = 0x02; + apdu.data = data; + apdu.datalen = apdu.lc = 1; + break; + default: + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); + break; + } + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + } + return ret; +} + +static int rutoken_delete_do(sc_card_t *card, u8 *pId) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + u8 data[1] = {*pId}; + sc_apdu_t apdu; + + if ((*pId < SC_RUTOKEN_DO_ALL_MIN_ID) || + (*pId > SC_RUTOKEN_DO_NOCHV_MAX_ID_V2)) + { + ret = SC_ERROR_INVALID_ARGUMENTS; + } + else + { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x64); + apdu.data = data; + apdu.datalen = apdu.lc = 1; + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_get_serial(sc_card_t *card, sc_serial_number_t *pSerial) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + sc_apdu_t apdu; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x81); + apdu.resp = pSerial->value; + apdu.le = 4; + apdu.resplen = sizeof(pSerial->value); + + if(sc_transmit_apdu(card, &apdu) >= 0) + { + pSerial->len = apdu.le; + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + } + + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +/* Both direction GOST cipher */ + +static int rutoken_cipher_p(sc_card_t *card, const u8 * crgram, size_t crgram_len, + u8 * out, size_t outlen, int p1, int p2, int isIV) +{ + const size_t cipher_chunk = 248; /* cipher_chunk <= SC_MAX_APDU_BUFFER_SIZE */ + size_t len, outlen_tail = outlen; + u8 *buf; + int ret; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + sc_debug(card->ctx, ": crgram_len %i; outlen %i\n", crgram_len, outlen); + + if (!out) + return SC_ERROR_INVALID_ARGUMENTS; + if (crgram_len < 16 || ((crgram_len) % 8)) + return SC_ERROR_WRONG_LENGTH; + + buf = malloc(cipher_chunk); + if (!buf) + return SC_ERROR_OUT_OF_MEMORY; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, p1, p2); + do + { + len = (crgram_len > cipher_chunk) ? cipher_chunk : crgram_len; + apdu.lc = len; + apdu.datalen = len; + apdu.data = crgram; + crgram += len; + crgram_len -= len; + + apdu.cla = (crgram_len == 0) ? 0x00 : 0x10; + apdu.le = len; + apdu.resplen = len; + apdu.resp = buf; + + if (sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + else + ret = SC_ERROR_CARD_CMD_FAILED; + if (ret == SC_NO_ERROR) + { + if (isIV) + { + apdu.resp += 8; + apdu.resplen -= 8; + isIV = 0; + } + if (apdu.resplen > outlen_tail) + ret = SC_ERROR_BUFFER_TOO_SMALL; + else + { + memcpy(out, apdu.resp, apdu.resplen); + out += apdu.resplen; + outlen_tail -= apdu.resplen; + } + } + } while (ret == SC_NO_ERROR && crgram_len != 0); + + free(buf); + + sc_debug(card->ctx, "len out cipher %d\n", outlen - outlen_tail); + if (ret == SC_NO_ERROR) + ret = (outlen_tail == 0) ? (int)outlen : SC_ERROR_WRONG_LENGTH; + + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +/* Launcher for chipher */ + +static int rutoken_cipher_gost(sc_card_t *card, + struct sc_rutoken_decipherinfo *ptr, char is_enchiper) +{ + int ret; + + if (is_enchiper) + ret = rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, + ptr->outbuf, ptr->outlen, 0x86, 0x80, 0); + else + ret = rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, + ptr->outbuf, ptr->outlen, 0x80, 0x86, 1); + if (ret > 0) + { + if ((size_t)ret == ptr->outlen) + ret = SC_SUCCESS; + else + ret = SC_ERROR_INTERNAL; /* SC_ERROR_DECRYPT_FAILED; */ + } + return ret; + +} + +static int rutoken_compute_mac_gost(sc_card_t *card, + const u8 *in, size_t ilen, + u8 *out, size_t olen) +{ + const size_t signing_chunk = 248; + size_t len; + int ret; + sc_apdu_t apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + if (!in || !out || olen != 4 || ilen == 0) + return SC_ERROR_INVALID_ARGUMENTS; + do + { + sc_format_apdu(card, &apdu, + ilen > signing_chunk ? + SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, + 0x2A, 0x90, 0x80); + len = (ilen > signing_chunk) ? signing_chunk : ilen; + apdu.lc = len; + apdu.datalen = len; + apdu.data = in; + in += len; + ilen -= len; + if (ilen == 0) + { + apdu.cla = 0x00; + apdu.le = olen; + apdu.resplen = olen; + apdu.resp = out; + } + else + apdu.cla = 0x10; + if (sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + else + ret = SC_ERROR_CARD_CMD_FAILED; + } while (ret == SC_NO_ERROR && ilen != 0); + + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +/* RSA emulation */ + +#ifdef HAVE_OPENSSL + +static int rutoken_get_current_fileid(sc_card_t *card, u8 id[2]) +{ + sc_apdu_t apdu; + int ret = SC_ERROR_CARD_CMD_FAILED; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x11); + apdu.resp = id; + apdu.resplen = sizeof(id); + apdu.le = 2; + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_read_prkey(sc_card_t *card, struct sc_pkcs15_prkey **out) +{ + int r; + u8 id[2]; + u8 *data; + sc_path_t path; + sc_file_t *file = NULL; + + r = sc_lock(card); + if(r != SC_SUCCESS) + return r; + + r = rutoken_get_current_fileid(card, id); + if(r == SC_SUCCESS) + { + sc_path_set(&path, SC_PATH_TYPE_FILE_ID, id, sizeof(id), 0, -1); + r = rutoken_select_file(card, &path, &file); + } + if(r == SC_SUCCESS && file) + { + data = malloc(file->size); + if(data == NULL) + r = SC_ERROR_OUT_OF_MEMORY; + else + { + r = sc_read_binary(card, 0, data, file->size, 0); + if(r > 0 && (size_t)r == file->size) + r = get_prkey_from_bin(data, file->size, out); + memset(data, 0, file->size); + free(data); + } + } + if(file) + sc_file_free(file); + sc_unlock(card); + return r; +} + +#define GETBN(bn) ((bn)->len? BN_bin2bn((bn)->data, (bn)->len, NULL) : NULL) + +static int extract_key(sc_card_t *card, EVP_PKEY **pk) +{ + struct sc_pkcs15_prkey *key = NULL; + int r; + + SC_FUNC_CALLED(card->ctx, 1); + + r = rutoken_read_prkey(card, &key); + + if (r < 0) + return r; + + if((*pk = EVP_PKEY_new()) == NULL) + r = SC_ERROR_OUT_OF_MEMORY; + else + { + switch (key->algorithm) + { + case SC_ALGORITHM_RSA: + { + RSA *rsa = RSA_new(); + + EVP_PKEY_set1_RSA(*pk, rsa); + rsa->n = GETBN(&key->u.rsa.modulus); + rsa->e = GETBN(&key->u.rsa.exponent); + rsa->d = GETBN(&key->u.rsa.d); + rsa->p = GETBN(&key->u.rsa.p); + rsa->q = GETBN(&key->u.rsa.q); + if((rsa->n == NULL) || (rsa->e == NULL) || (rsa->d == NULL) || + (rsa->p == NULL) || (rsa->q == NULL)) + r = SC_ERROR_INTERNAL; + RSA_free(rsa); + break; + } + default: + r = SC_ERROR_NOT_SUPPORTED; + } + } + if ((r < 0) && (*pk != NULL)) + { + EVP_PKEY_free(*pk); + *pk = NULL; + } + if(key) sc_pkcs15_free_prkey(key); + return r; +} + +static int sign_ext(sc_card_t *card, const u8 *data, size_t len, u8 *out, size_t out_len) +{ + EVP_PKEY *pkey = NULL; + int ret, r; + + SC_FUNC_CALLED(card->ctx, 1); + + if (out_len < len) + return SC_ERROR_INVALID_ARGUMENTS; + + ret = extract_key(card, &pkey); + if (ret == SC_SUCCESS) + { + r = RSA_private_encrypt(len, data, out, pkey->pkey.rsa, RSA_PKCS1_PADDING); + if ( r < 0) + { + ret = SC_ERROR_INTERNAL; + char error[1024]; + ERR_load_crypto_strings(); + ERR_error_string(ERR_get_error(), error); + sc_error(card->ctx, error); + ERR_free_strings(); + } + } + if(pkey) + EVP_PKEY_free(pkey); + return ret; +} + +static int decipher_ext(sc_card_t *card, const u8 *data, size_t len, u8 *out, size_t out_len) +{ + EVP_PKEY *pkey = NULL; + int ret; + + SC_FUNC_CALLED(card->ctx, 1); + + if (out_len < len) + return SC_ERROR_INVALID_ARGUMENTS; + + ret = extract_key(card, &pkey); + if (ret == SC_SUCCESS) + { + ret = RSA_private_decrypt(len, data, out, pkey->pkey.rsa, RSA_PKCS1_PADDING); + if ( ret < 0) + { + ret = SC_ERROR_INTERNAL; + char error[1024]; + ERR_load_crypto_strings(); + ERR_error_string(ERR_get_error(), error); + sc_error(card->ctx, error); + ERR_free_strings(); + } + } + if(pkey) + EVP_PKEY_free(pkey); + return ret; +} + +static int rutoken_decipher(sc_card_t *card, + const u8 * data, size_t datalen, + u8 * out, size_t outlen) +{ + auth_senv_t *senv = (auth_senv_t *)card->drv_data; + + SC_FUNC_CALLED(card->ctx, 1); + + if (!senv) + return SC_ERROR_INTERNAL; + + if(senv->algorithm == SC_ALGORITHM_GOST) + { + return rutoken_cipher_p(card, data, datalen, out, outlen, 0x80, 0x86, 1); + } + else if (senv->algorithm == SC_ALGORITHM_RSA_RAW) + { + return decipher_ext(card, data, datalen, out, outlen); + } + else + return SC_ERROR_NOT_SUPPORTED; +} + +static int rutoken_compute_signature(struct sc_card *card, + const u8 * data, size_t datalen, + u8 * out, size_t outlen) +{ + auth_senv_t *senv = (auth_senv_t *)card->drv_data; + + SC_FUNC_CALLED(card->ctx, 1); + + if (!senv) + return SC_ERROR_INTERNAL; + + if (senv->algorithm == SC_ALGORITHM_GOST) + { + return rutoken_compute_mac_gost(card, data, datalen, out, outlen); + } + else if (senv->algorithm == SC_ALGORITHM_RSA_RAW) + { + return sign_ext(card, data, datalen, out, outlen); + } + else + return SC_ERROR_NOT_SUPPORTED; +} +#endif + +static int rutoken_get_challenge(sc_card_t *card, u8 *rnd, size_t count) +{ + int ret = SC_NO_ERROR; + sc_apdu_t apdu; + u8 rbuf[32]; + size_t n; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0x00, 0x00); + apdu.le = sizeof(rbuf); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + + while (count > 0) + { + ret = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, ret, "APDU transmit failed"); + if (apdu.resplen != sizeof(rbuf)) + return rutoken_check_sw(card, apdu.sw1, apdu.sw2); + n = count < sizeof(rbuf) ? count : sizeof(rbuf); + memcpy(rnd, rbuf, n); + count -= n; + rnd += n; + } + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_get_info(sc_card_t *card, void *buff) +{ + sc_apdu_t apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + int r = SC_ERROR_CARD_CMD_FAILED; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x89); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 256; + if(sc_transmit_apdu(card, &apdu) >= 0) + { + memcpy(buff, apdu.resp, apdu.resplen); + r = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 1, r); +} + +static int rutoken_format(sc_card_t *card, int apdu_ins) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + sc_apdu_t apdu; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, apdu_ins, 0x00, 0x00); + apdu.cla = 0x80; + if(sc_transmit_apdu(card, &apdu) >= 0) + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +static int rutoken_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) +{ + int ret = (ptr != NULL + /*|| cmd == SC_CARDCTL_ERASE_CARD */ + || cmd == SC_CARDCTL_RUTOKEN_FORMAT_INIT + || cmd == SC_CARDCTL_RUTOKEN_FORMAT_END + ) ? SC_NO_ERROR : SC_ERROR_INVALID_ARGUMENTS; + + SC_FUNC_CALLED(card->ctx, 1); + + if(ret == SC_NO_ERROR) + { + switch (cmd) + { + case SC_CARDCTL_RUTOKEN_CREATE_DO: + ret = rutoken_create_do(card, ptr); + break; + case SC_CARDCTL_RUTOKEN_GENERATE_KEY_DO: + ret = rutoken_key_gen(card, ptr); + break; + case SC_CARDCTL_RUTOKEN_DELETE_DO: + ret = rutoken_delete_do(card, ptr); + break; + case SC_CARDCTL_RUTOKEN_GET_DO_INFO: + ret = rutoken_get_do_info(card, ptr); + break; + case SC_CARDCTL_GET_SERIALNR: + ret = rutoken_get_serial(card, ptr); + break; + case SC_CARDCTL_RUTOKEN_CHANGE_DO: + ret = SC_ERROR_NOT_SUPPORTED; + break; + case SC_CARDCTL_RUTOKEN_GET_INFO: + ret = rutoken_get_info(card, ptr); + break; + case SC_CARDCTL_RUTOKEN_GOST_ENCIPHER: + ret = rutoken_cipher_gost(card, ptr, 1); + break; + case SC_CARDCTL_RUTOKEN_GOST_DECIPHER: + ret = rutoken_cipher_gost(card, ptr, 0); + break; + /* case SC_CARDCTL_ERASE_CARD: */ + case SC_CARDCTL_RUTOKEN_FORMAT_INIT: + /* ret = rutoken_format(card, 0x7a); *//* APDU: INIT RUTOKEN */ + ret = rutoken_format(card, 0x8a); /* APDU: NEW INIT RUTOKEN */ + break; + case SC_CARDCTL_RUTOKEN_FORMAT_END: + ret = rutoken_format(card, 0x7b); /* APDU: FORMAT END */ + break; + default: + sc_debug(card->ctx, "cmd = %d", cmd); +#if 0 + { + sc_apdu_t *pApdu = ptr; + if(sc_transmit_apdu(card, pApdu) >= 0) + ret = rutoken_check_sw(card, pApdu->sw1, pApdu->sw2); + else + ret = SC_ERROR_CARD_CMD_FAILED; + break; + } +#endif + ret = SC_ERROR_NOT_SUPPORTED; + break; + } + } + return ret; +} + +static struct sc_card_driver * sc_get_driver(void) +{ + if (iso_ops == NULL) + iso_ops = sc_get_iso7816_driver()->ops; + rutoken_ops = *iso_ops; + + rutoken_ops.match_card = rutoken_match_card; + rutoken_ops.init = rutoken_init; + rutoken_ops.finish = rutoken_finish; + rutoken_ops.check_sw = rutoken_check_sw; + rutoken_ops.select_file = rutoken_select_file; + rutoken_ops.create_file = rutoken_create_file; + rutoken_ops.delete_file = rutoken_delete_file; + rutoken_ops.list_files = rutoken_list_files; + rutoken_ops.card_ctl = rutoken_card_ctl; + rutoken_ops.get_challenge = rutoken_get_challenge; + + #ifdef HAVE_OPENSSL + rutoken_ops.decipher = rutoken_decipher; + rutoken_ops.compute_signature = rutoken_compute_signature; + #endif + rutoken_ops.set_security_env = rutoken_set_security_env; + rutoken_ops.restore_security_env = rutoken_restore_security_env; + + rutoken_ops.verify = rutoken_verify; + rutoken_ops.logout = rutoken_logout; + rutoken_ops.change_reference_data = rutoken_change_reference_data; + rutoken_ops.reset_retry_counter = rutoken_reset_retry_counter; + + rutoken_ops.pin_cmd = NULL; + rutoken_ops.read_record = NULL; + rutoken_ops.write_record = NULL; + rutoken_ops.append_record = NULL; + rutoken_ops.update_record = NULL; + rutoken_ops.write_binary = NULL; + + return &rutoken_drv; +} + +struct sc_card_driver * sc_get_rutoken_driver(void) +{ + return sc_get_driver(); +} + diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index 2b73605..76c97a1 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -139,7 +139,24 @@ enum { SC_CARDCTL_ASEPCOS_CHANGE_KEY, SC_CARDCTL_ASEPCOS_AKN2FILEID, SC_CARDCTL_ASEPCOS_SET_SATTR, - SC_CARDCTL_ASEPCOS_ACTIVATE_FILE + SC_CARDCTL_ASEPCOS_ACTIVATE_FILE, + + /* + * ruToken spcific calls + */ + SC_CARDCTL_RUTOKEN_BASE = _CTL_PREFIX('R', 'T', 'K'), + /* PUT_DATA */ + SC_CARDCTL_RUTOKEN_CREATE_DO, + SC_CARDCTL_RUTOKEN_CHANGE_DO, + SC_CARDCTL_RUTOKEN_GENERATE_KEY_DO, + SC_CARDCTL_RUTOKEN_DELETE_DO, + SC_CARDCTL_RUTOKEN_GET_INFO, + /* NON STANDART */ + SC_CARDCTL_RUTOKEN_GET_DO_INFO, + SC_CARDCTL_RUTOKEN_GOST_ENCIPHER, + SC_CARDCTL_RUTOKEN_GOST_DECIPHER, + SC_CARDCTL_RUTOKEN_FORMAT_INIT, + SC_CARDCTL_RUTOKEN_FORMAT_END }; enum { @@ -357,9 +374,6 @@ struct sc_cardctl_setcos_data_obj { int LengthMax; }; -#define OP_TYPE_GENERATE 0 -#define OP_TYPE_STORE 1 - struct sc_cardctl_setcos_gen_store_key_info { int op_type; unsigned int mod_len; /* in bits */ @@ -426,6 +440,119 @@ typedef struct sc_cardctl_asepcos_activate_file { int is_ef; } sc_cardctl_asepcos_activate_file_t; +#define OP_TYPE_GENERATE 0 +#define OP_TYPE_STORE 1 + +/* + * RuToken types and constants + */ + +#define SC_RUTOKEN_DO_PART_BODY_LEN 199 +#define SC_RUTOKEN_DO_HDR_LEN 32 + +/* DO Types */ +#define SC_RUTOKEN_TYPE_MASK 0xF +#define SC_RUTOKEN_TYPE_SE 0x0 +#define SC_RUTOKEN_TYPE_CHV 0x1 +#define SC_RUTOKEN_TYPE_KEY 0x2 + +#define SC_RUTOKEN_COMPACT_DO_MAX_LEN 16 /* MAX Body length of Compact DOs */ + +#define SC_RUTOKEN_DO_ALL_MIN_ID 0x1 /* MIN ID value of All DOs */ +#define SC_RUTOKEN_DO_CHV_MAX_ID 0x1F /* MAX ID value of CHV-objects */ +#define SC_RUTOKEN_DO_NOCHV_MAX_ID 0xFE /* MAX ID value of All Other DOs */ + +/* DO Default Lengths */ +#define SC_RUTOKEN_DEF_LEN_DO_GOST 32 +#define SC_RUTOKEN_DEF_LEN_DO_SE 6 + + +#define SC_RUTOKEN_ALLTYPE_SE SC_RUTOKEN_TYPE_SE /* SE */ +#define SC_RUTOKEN_ALLTYPE_GCHV SC_RUTOKEN_TYPE_CHV /* GCHV */ +#define SC_RUTOKEN_ALLTYPE_LCHV 0x11 /* LCHV */ +#define SC_RUTOKEN_ALLTYPE_GOST SC_RUTOKEN_TYPE_KEY /* GOST */ + +/* DO ID */ +#define SC_RUTOKEN_ID_CURDF_RESID_FLAG 0x80 /* DO placed in current DF */ + +#define SC_RUTOKEN_DEF_ID_GCHV_ADMIN 0x01 /* ID DO ADMIN */ +#define SC_RUTOKEN_DEF_ID_GCHV_USER 0x02 /* ID DO USER */ + +/* DO Options */ +#define SC_RUTOKEN_OPTIONS_GCHV_ACCESS_MASK 0x7 /* Access rights */ +#define SC_RUTOKEN_OPTIONS_GACCESS_ADMIN SC_RUTOKEN_DEF_ID_GCHV_ADMIN /* ADMIN */ +#define SC_RUTOKEN_OPTIONS_GACCESS_USER SC_RUTOKEN_DEF_ID_GCHV_USER /* USER */ + +#define SC_RUTOKEN_OPTIONS_GOST_CRYPT_MASK 0x7 /* crypto algorithm */ +#define SC_RUTOKEN_OPTIONS_GOST_CRYPT_PZ 0x0 /* (encryptECB) simple-change mode */ +#define SC_RUTOKEN_OPTIONS_GOST_CRYPT_GAMM 0x1 /* (encryptCNT) gamma mode */ +#define SC_RUTOKEN_OPTIONS_GOST_CRYPT_GAMMOS 0x2 /* (encryptCFB) feed-back gamma mode */ + + +/* DO flags */ +#define SC_RUTOKEN_FLAGS_COMPACT_DO 0x1 +#define SC_RUTOKEN_FLAGS_OPEN_DO_MASK 0x6 +#define SC_RUTOKEN_FLAGS_BLEN_OPEN_DO 0x2 +#define SC_RUTOKEN_FLAGS_FULL_OPEN_DO 0x6 + +/* DO MAX:CUR try */ +#define SC_RUTOKEN_MAXTRY_MASK 0xF0 /* MAX try */ +#define SC_RUTOKEN_CURTRY_MASK 0x0F /* CUR try */ + +#define SC_RUTOKEN_DO_CHV_MAX_ID_V2 SC_RUTOKEN_DEF_ID_GCHV_USER /* MAX ID value of CHV-objects */ +#define SC_RUTOKEN_DO_NOCHV_MAX_ID_V2 SC_RUTOKEN_DO_NOCHV_MAX_ID /* MAX ID value of All Other DOs */ + +#define SEC_ATTR_SIZE 15 + +#pragma pack(push, 1) +typedef u8 sc_SecAttrV2_t[SEC_ATTR_SIZE]; + +typedef struct sc_ObjectTypeID{ + u8 byObjectType; + u8 byObjectID; +} sc_ObjectTypeID_t; + +typedef struct sc_ObjectParams{ + u8 byObjectOptions; + u8 byObjectFlags; + u8 byObjectTry; +} sc_ObjectParams_t; + +typedef struct sc_DOHdrV2 { + unsigned short wDOBodyLen; + sc_ObjectTypeID_t OTID; + sc_ObjectParams_t OP; + u8 dwReserv1[4]; + u8 abyReserv2[6]; + sc_SecAttrV2_t SA_V2; +} sc_DOHdrV2_t; + +typedef struct sc_DO_V2 { + sc_DOHdrV2_t HDR; + u8 abyDOBody[SC_RUTOKEN_DO_PART_BODY_LEN]; +} sc_DO_V2_t; + +typedef enum +{ + select_first, + select_by_id, + select_next, +} SC_RUTOKEN_DO_SEL_TYPES; + +typedef struct sc_DO_INFO_V2 { + u8 DoId; + SC_RUTOKEN_DO_SEL_TYPES SelType; + u8 pDoData[256]; +} sc_DO_INFO_t; + +struct sc_rutoken_decipherinfo { + const u8 *inbuf; + size_t inlen; + u8 *outbuf; + size_t outlen; +}; +#pragma pack(pop) + #ifdef __cplusplus } #endif diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index ddb3b5a..5376ed1 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -51,6 +51,7 @@ struct _sc_driver_entry { static const struct _sc_driver_entry internal_card_drivers[] = { /* legacy, the old name was "etoken", so we keep that for a while */ + { "rutoken", (void *(*)(void)) sc_get_rutoken_driver }, { "cardos", (void *(*)(void)) sc_get_cardos_driver }, { "etoken", (void *(*)(void)) sc_get_cardos_driver }, { "flex", (void *(*)(void)) sc_get_cryptoflex_driver }, diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index eaa15ba..f9a871f 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -151,10 +151,12 @@ extern "C" { /* Symmetric algorithms */ #define SC_ALGORITHM_DES 64 #define SC_ALGORITHM_3DES 65 +#define SC_ALGORITHM_GOST 66 /* Hash algorithms */ #define SC_ALGORITHM_MD5 128 #define SC_ALGORITHM_SHA1 129 +#define SC_ALGORITHM_GOSTHASH 130 /* Key derivation algorithms */ #define SC_ALGORITHM_PBKDF2 192 @@ -188,6 +190,10 @@ extern "C" { #define SC_ALGORITHM_RSA_HASH_SHA224 0x00001000 #define SC_ALGORITHM_RSA_HASHES 0x00001FE0 +#define SC_ALGORITHM_GOST_CRYPT_PZ 0x0 +#define SC_ALGORITHM_GOST_CRYPT_GAMM 0x1 +#define SC_ALGORITHM_GOST_CRYPT_GAMMOS 0x2 + /* Event masks for sc_wait_for_event() */ #define SC_EVENT_CARD_INSERTED 0x0001 #define SC_EVENT_CARD_REMOVED 0x0002 @@ -1154,6 +1160,7 @@ extern struct sc_reader_driver *sc_get_pcsc_driver(void); extern struct sc_reader_driver *sc_get_ctapi_driver(void); extern struct sc_reader_driver *sc_get_openct_driver(void); +extern sc_card_driver_t *sc_get_rutoken_driver(void); extern sc_card_driver_t *sc_get_default_driver(void); extern sc_card_driver_t *sc_get_emv_driver(void); extern sc_card_driver_t *sc_get_cardos_driver(void); diff --git a/src/libopensc/pkcs15-algo.c b/src/libopensc/pkcs15-algo.c index 1a766c1..ea546e6 100644 --- a/src/libopensc/pkcs15-algo.c +++ b/src/libopensc/pkcs15-algo.c @@ -233,6 +233,12 @@ static struct sc_asn1_pkcs15_algorithm_info algorithm_table[] = { asn1_encode_des_params, free }, #endif +#ifdef SC_ALGORITHM_GOST /* EDE CBC mode */ + { SC_ALGORITHM_GOST, {{ 1, 2, 4434, 66565, 3, 7 }}, + NULL, + NULL, + NULL }, +#endif /* We do not support PBES1 because the encryption is weak */ #ifdef SC_ALGORITHM_PBKDF2 { SC_ALGORITHM_PBKDF2, {{ 1, 2, 840, 113549, 1, 5, 12 }}, diff --git a/src/libopensc/pkcs15-prkey-rutoken.c b/src/libopensc/pkcs15-prkey-rutoken.c new file mode 100644 index 0000000..ed39c15 --- /dev/null +++ b/src/libopensc/pkcs15-prkey-rutoken.c @@ -0,0 +1,364 @@ +/* + * ruToken specific operation for PKCS #15 private key functions + * + * Copyright (C) 2007 Pavel Mironchik + * Copyright (C) 2007 Eugene Hermann + * + * 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 +#endif +#include +#include +#include +#include +#include +#include + +/* BLOB definition */ + +typedef struct _RSAPUBKEY { + u_int32_t magic; + u_int32_t bitlen; + u_int32_t pubexp; +} RSAPUBKEY; + +typedef struct _PUBLICKEYSTRUC { + u8 bType; + u8 bVersion; + u_int16_t reserved; + u_int32_t aiKeyAlg; +} BLOBHEADER; + +typedef struct _PRIVATEKEYBLOB { + BLOBHEADER blobheader; + RSAPUBKEY rsapubkey; + u8 *modulus; + u8 *prime1; + u8 *prime2; + u8 *exponent1; + u8 *exponent2; + u8 *coefficient; + u8 *privateExponent; +} PRIVATEKEYBLOB; + + +static void ArrayReverse(u8 *buf, size_t size) +{ + size_t i; + u8 tmp; + + for (i=0; i < size/2; ++i) + { + tmp = buf[i]; + buf[i] = buf[size-1-i]; + buf[size-1-i] = tmp; + } +} + +static int free_private_blob(PRIVATEKEYBLOB *pr_blob) +{ + free(pr_blob->modulus); + free(pr_blob->prime1); + free(pr_blob->prime2); + free(pr_blob->exponent1); + free(pr_blob->exponent2); + free(pr_blob->coefficient); + free(pr_blob->privateExponent); + return 0; +} + +static int bin_to_private_blob(PRIVATEKEYBLOB *pr_blob, const u8* buf, size_t buf_len) +{ + const u8 *tmp; + size_t len = 2 + sizeof(pr_blob->blobheader) + sizeof(pr_blob->rsapubkey); + + if (buf_len < len) + return -1; + + tmp = buf + 2; + memcpy(&pr_blob->blobheader, tmp, sizeof(pr_blob->blobheader)); + tmp += sizeof(pr_blob->blobheader); + + memcpy(&pr_blob->rsapubkey, tmp, sizeof(pr_blob->rsapubkey)); + tmp += sizeof(pr_blob->rsapubkey); + + u_int32_t bitlen = pr_blob->rsapubkey.bitlen; + + len += bitlen/8 * 2 + bitlen/16 * 5; + if (buf_len < len) + return -1; + + pr_blob->modulus = malloc(bitlen/8); + pr_blob->prime1 = malloc(bitlen/16); + pr_blob->prime2 = malloc(bitlen/16); + pr_blob->exponent1 = malloc(bitlen/16); + pr_blob->exponent2 = malloc(bitlen/16); + pr_blob->coefficient = malloc(bitlen/16); + pr_blob->privateExponent = malloc(bitlen/8); + if (!pr_blob->modulus || !pr_blob->prime1 || !pr_blob->prime2 + || !pr_blob->exponent1 || !pr_blob->exponent2 + || !pr_blob->coefficient || !pr_blob->privateExponent + ) + { + free_private_blob(pr_blob); + return -1; + } + memcpy(pr_blob->modulus, tmp, bitlen/8); + tmp += bitlen/8; + memcpy(pr_blob->prime1, tmp, bitlen/16); + tmp += bitlen/16; + memcpy(pr_blob->prime2, tmp, bitlen/16); + tmp += bitlen/16; + memcpy(pr_blob->exponent1, tmp, bitlen/16); + tmp += bitlen/16; + memcpy(pr_blob->exponent2, tmp, bitlen/16); + tmp += bitlen/16; + memcpy(pr_blob->coefficient, tmp, bitlen/16); + tmp += bitlen/16; + memcpy(pr_blob->privateExponent, tmp, bitlen/8); + tmp += bitlen/8; + return 0; +} + +static int create_private_blob(PRIVATEKEYBLOB *pr_blob, const struct sc_pkcs15_prkey_rsa *key) +{ + size_t n; + const u_int32_t bitlen = key->modulus.len*8; + + if ( key->modulus.len != bitlen/8 + || key->p.len != bitlen/16 + || key->q.len != bitlen/16 + || key->dmp1.len != bitlen/16 + || key->dmq1.len != bitlen/16 + || key->iqmp.len != bitlen/16 + || key->d.len != bitlen/8 + ) + return -1; + + /* blobheader */ + /* u8 bType; */ + pr_blob->blobheader.bType = 0x07; + /* u8 bVersion; */ + pr_blob->blobheader.bVersion = 0x02; + /* u16 reserved; */ + pr_blob->blobheader.reserved = 0; + /* u32 aiKeyAlg; */ + pr_blob->blobheader.aiKeyAlg = 0x0000a400; + + pr_blob->rsapubkey.magic = 0x32415352; /* "RSA2" */ + pr_blob->rsapubkey.bitlen = bitlen; + + pr_blob->rsapubkey.pubexp = 0; + for (n=0; n < key->exponent.len && n < sizeof(pr_blob->rsapubkey.pubexp); ++n) + pr_blob->rsapubkey.pubexp += + (u_int32_t)key->exponent.data[key->exponent.len - n - 1] << 8*n; + + pr_blob->modulus = malloc(bitlen/8); + pr_blob->prime1 = malloc(bitlen/16); + pr_blob->prime2 = malloc(bitlen/16); + pr_blob->exponent1 = malloc(bitlen/16); + pr_blob->exponent2 = malloc(bitlen/16); + pr_blob->coefficient = malloc(bitlen/16); + pr_blob->privateExponent = malloc(bitlen/8); + if (!pr_blob->modulus || !pr_blob->prime1 || !pr_blob->prime2 + || !pr_blob->exponent1 || !pr_blob->exponent2 + || !pr_blob->coefficient || !pr_blob->privateExponent + ) + { + free_private_blob(pr_blob); + return -1; + } + + memcpy(pr_blob->modulus, key->modulus.data, key->modulus.len); + ArrayReverse(pr_blob->modulus, key->modulus.len); + memcpy(pr_blob->prime1, key->p.data, key->p.len); + ArrayReverse(pr_blob->prime1, key->p.len); + memcpy(pr_blob->prime2, key->q.data, key->q.len); + ArrayReverse(pr_blob->prime2, key->q.len); + memcpy(pr_blob->exponent1, key->dmp1.data, key->dmp1.len); + ArrayReverse(pr_blob->exponent1, key->dmp1.len); + memcpy(pr_blob->exponent2, key->dmq1.data, key->dmq1.len); + ArrayReverse(pr_blob->exponent2, key->dmq1.len); + memcpy(pr_blob->coefficient, key->iqmp.data, key->iqmp.len); + ArrayReverse(pr_blob->coefficient, key->iqmp.len); + memcpy(pr_blob->privateExponent, key->d.data, key->d.len); + ArrayReverse(pr_blob->privateExponent, key->d.len); + return 0; +} + +static int get_sc_pksc15_prkey_rsa(const PRIVATEKEYBLOB *pr_blob, struct sc_pkcs15_prkey_rsa *key) +{ + static const u8 Exp[3] = { 0x01, 0x00, 0x01 }; /* big endian */ + + const u_int32_t bitlen = pr_blob->rsapubkey.bitlen; + + key->modulus.data = malloc(bitlen/8); + key->modulus.len = bitlen/8; + key->p.data = malloc(bitlen/16); + key->p.len = bitlen/16; + key->q.data = malloc(bitlen/16); + key->q.len = bitlen/16; + key->dmp1.data = malloc(bitlen/16); + key->dmp1.len = bitlen/16; + key->dmq1.data = malloc(bitlen/16); + key->dmq1.len = bitlen/16; /* ?! bitlen/16 - 1; */ + key->iqmp.data = malloc(bitlen/16); + key->iqmp.len = bitlen/16; + key->d.data = malloc(bitlen/8); + key->d.len = bitlen/8; + key->exponent.data = malloc(sizeof(Exp)); + key->exponent.len = sizeof(Exp); + if(!key->modulus.data || !key->p.data || !key->q.data || !key->dmp1.data + || !key->dmq1.data || !key->iqmp.data || !key->d.data + || !key->exponent.data + ) + { + free(key->modulus.data); + free(key->p.data); + free(key->q.data); + free(key->dmp1.data); + free(key->dmq1.data); + free(key->iqmp.data); + free(key->d.data); + free(key->exponent.data); + memset(key, 0, sizeof(*key)); + return -1; + } + + memcpy(key->exponent.data, &Exp, sizeof(Exp)); + memcpy(key->modulus.data, pr_blob->modulus, key->modulus.len); + ArrayReverse(key->modulus.data, key->modulus.len); + memcpy(key->p.data, pr_blob->prime1, key->p.len); + ArrayReverse(key->p.data, key->p.len); + memcpy(key->q.data, pr_blob->prime2, key->q.len); + ArrayReverse(key->q.data, key->q.len); + memcpy(key->dmp1.data, pr_blob->exponent1, key->dmp1.len); + ArrayReverse(key->dmp1.data, key->dmp1.len); + memcpy(key->dmq1.data, pr_blob->exponent2, key->dmq1.len); + ArrayReverse(key->dmq1.data, key->dmq1.len); + memcpy(key->iqmp.data, pr_blob->coefficient, key->iqmp.len); + ArrayReverse(key->iqmp.data, key->iqmp.len); + memcpy(key->d.data, pr_blob->privateExponent, key->d.len); + ArrayReverse(key->d.data, key->d.len); + return 0; +} + +static int private_blob_to_bin(const PRIVATEKEYBLOB *pr_blob, u8 *buf, size_t *buf_len) +{ + size_t len = 2 + sizeof(pr_blob->blobheader) + sizeof(pr_blob->rsapubkey); + + if(*buf_len < len) + return -1; + + buf[0] = 2; + buf[1] = 1; + u8 *tmp = buf + 2; + memcpy(tmp, &pr_blob->blobheader, sizeof(pr_blob->blobheader)); + tmp += sizeof(pr_blob->blobheader); + + memcpy(tmp, &pr_blob->rsapubkey, sizeof(pr_blob->rsapubkey)); + tmp += sizeof(pr_blob->rsapubkey); + + len += pr_blob->rsapubkey.bitlen/8 * 2 + pr_blob->rsapubkey.bitlen/16 * 5; + if (*buf_len < len) + return -1; + + memcpy(tmp, pr_blob->modulus, pr_blob->rsapubkey.bitlen/8); + tmp += pr_blob->rsapubkey.bitlen/8; + + memcpy(tmp, pr_blob->prime1, pr_blob->rsapubkey.bitlen/16); + tmp += pr_blob->rsapubkey.bitlen/16; + + memcpy(tmp, pr_blob->prime2, pr_blob->rsapubkey.bitlen/16); + tmp += pr_blob->rsapubkey.bitlen/16; + + memcpy(tmp, pr_blob->exponent1, pr_blob->rsapubkey.bitlen/16); + tmp += pr_blob->rsapubkey.bitlen/16; + + memcpy(tmp, pr_blob->exponent2, pr_blob->rsapubkey.bitlen/16); + tmp += pr_blob->rsapubkey.bitlen/16; + + memcpy(tmp, pr_blob->coefficient, pr_blob->rsapubkey.bitlen/16); + tmp += pr_blob->rsapubkey.bitlen/16; + + memcpy(tmp, pr_blob->privateExponent, pr_blob->rsapubkey.bitlen/8); + tmp += pr_blob->rsapubkey.bitlen/8; + + *buf_len = len; + return 0; +} + +static int clean_prkey_private_blob(const PRIVATEKEYBLOB* pr_blob) +{ + const u_int32_t bitlen = pr_blob->rsapubkey.bitlen; + + memset(pr_blob->modulus, 0, bitlen/8); + memset(pr_blob->prime1, 0, bitlen/16); + memset(pr_blob->prime2, 0, bitlen/16); + memset(pr_blob->exponent1, 0, bitlen/16); + memset(pr_blob->exponent2, 0, bitlen/16); + memset(pr_blob->coefficient, 0, bitlen/16); + memset(pr_blob->privateExponent, 0, bitlen/8); + return 0; +} + +int get_prkey_from_bin(const u8 *data, size_t len, struct sc_pkcs15_prkey **key) +{ + int ret = -1; + PRIVATEKEYBLOB pr_blob; + + if (data && key) + { + *key = malloc(sizeof(struct sc_pkcs15_prkey)); + if (*key) + { + memset(*key, 0, sizeof(**key)); + ret = bin_to_private_blob(&pr_blob, data, len); + if (ret == 0) + { + ret = get_sc_pksc15_prkey_rsa(&pr_blob, &(*key)->u.rsa); + if (ret == 0) + (*key)->algorithm = SC_ALGORITHM_RSA; + clean_prkey_private_blob(&pr_blob); + free_private_blob(&pr_blob); + memset(&pr_blob, 0, sizeof(pr_blob)); + } + } + } + return ret; +} + +int get_bin_from_prkey(const struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize) +{ + int r = -1; + PRIVATEKEYBLOB prkeyblob; + + if (rsa && key && keysize) + { + r = create_private_blob(&prkeyblob, rsa); + if (r == 0) + { + r = private_blob_to_bin(&prkeyblob, key, keysize); + clean_prkey_private_blob(&prkeyblob); + free_private_blob(&prkeyblob); + memset(&prkeyblob, 0, sizeof(prkeyblob)); + } + } + return r; +} + diff --git a/src/libopensc/pkcs15-rutoken.c b/src/libopensc/pkcs15-rutoken.c new file mode 100644 index 0000000..a1b895c --- /dev/null +++ b/src/libopensc/pkcs15-rutoken.c @@ -0,0 +1,216 @@ +/* + * PKCS15 emulation layer for ruToken + * + * Copyright (C) 2007 Pavel Mironchik + * Copyright (C) 2007 Eugene Hermann + * + * 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 +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RUT_LABEL "ruToken card" + +#define PrKDF_path "3F00FF000001" +#define PuKDF_path "3F00FF000002" +#define CDF_path "3F00FF000003" +#define DODF_path "3F00FF000004" +#define AODF_path "3F00FF00A0DF" + +static const struct +{ + char const* path; + unsigned int type; +} arr_profile_df[] = + { + { PrKDF_path, SC_PKCS15_PRKDF }, + { PuKDF_path, SC_PKCS15_PUKDF }, + { CDF_path, SC_PKCS15_CDF }, + { DODF_path, SC_PKCS15_DODF }, + { AODF_path, SC_PKCS15_AODF } + }; + +static const struct { + int reference; + const char *label; + unsigned int flags; +} pinlist[]= +{ + { SC_RUTOKEN_DEF_ID_GCHV_USER, "User PIN", + SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_CASE_SENSITIVE + }, + { SC_RUTOKEN_DEF_ID_GCHV_ADMIN, "SO PIN", + SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_CASE_SENSITIVE + | SC_PKCS15_PIN_FLAG_SO_PIN + } +}; + +static int add_predefined_pin(sc_pkcs15_card_t *p15card, sc_path_t *adf_path) +{ + size_t i; + sc_pkcs15_pin_info_t *pin_info; + sc_pkcs15_object_t *pin_obj; + + for (i = 0; i < sizeof(pinlist)/sizeof(pinlist[0]); ++i) + { + pin_info = calloc(1, sizeof(*pin_info)); + pin_obj = calloc(1, sizeof(*pin_obj)); + if (!pin_info || !pin_obj) + { + free(pin_info); + free(pin_obj); + return SC_ERROR_OUT_OF_MEMORY; + } + pin_info->auth_id.len = 1; + pin_info->auth_id.value[0] = (u8)pinlist[i].reference; + pin_info->reference = pinlist[i].reference; + pin_info->flags = pinlist[i].flags; + pin_info->type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; + pin_info->min_length = 1; + pin_info->stored_length = 16; + pin_info->max_length = 16; + pin_info->pad_char = -1; + pin_info->tries_left = 1; + pin_info->path = *adf_path; + + strncpy(pin_obj->label, pinlist[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); + pin_obj->flags = SC_PKCS15_CO_FLAG_PRIVATE; + + sc_pkcs15emu_add_pin_obj(p15card, pin_obj, pin_info); + free(pin_obj); + free(pin_info); + } + return SC_SUCCESS; +} + +static void set_string(char **strp, const char *value) +{ + if (*strp) free(*strp); + *strp = value ? strdup(value) : NULL; +} + +static int set_card_info(sc_pkcs15_card_t *p15card) +{ + sc_card_t *card = p15card->card; + sc_context_t *ctx = p15card->card->ctx; + sc_serial_number_t serialnr; + char serial[30] = {0}; + u8 info[8]; + + /* get the card serial number */ + if (sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serialnr) < 0) + { + sc_debug(ctx, "unable to get ICCSN"); + return SC_ERROR_WRONG_CARD; + } + sc_bin_to_hex(serialnr.value, serialnr.len , serial, sizeof(serial), 0); + set_string(&p15card->serial_number, serial); + + /* get ruToken information */ + if (sc_card_ctl(card, SC_CARDCTL_RUTOKEN_GET_INFO, info) < 0) + { + sc_debug(ctx, "unable to get token information"); + return SC_ERROR_WRONG_CARD; + } + set_string(&p15card->label, RUT_LABEL); + p15card->version = (info[1] >> 4)*10 + (info[1] & 0x0f); + sc_bin_to_hex(info + 3, 3 , serial, sizeof(serial), 0); + set_string(&p15card->manufacturer_id, serial); + + return SC_SUCCESS; +} + +static int sc_pkcs15_rutoken_init_func(sc_pkcs15_card_t *p15card) +{ + sc_context_t *ctx; + sc_file_t *df; + sc_card_t *card; + sc_path_t path; + size_t i; + int r; + unsigned int added_pin = 0; + + if (!p15card || !p15card->card || !p15card->card->ctx + || !p15card->card->ops + || !p15card->card->ops->select_file + ) + return SC_ERROR_INVALID_ARGUMENTS; + + card = p15card->card; + ctx = card->ctx; + + r = set_card_info(p15card); + if (r != SC_SUCCESS) + { + sc_error(ctx, "Do not set card info: %s\n", sc_strerror(r)); + r = SC_SUCCESS; + } + + for (i = 0; i < sizeof(arr_profile_df)/sizeof(arr_profile_df[0]); ++i) + { + df = NULL; + sc_format_path(arr_profile_df[i].path, &path); + if (card->ops->select_file(card, &path, &df) == SC_ERROR_FILE_NOT_FOUND) + { + sc_error(ctx, "File system mismatch\n"); + r = SC_ERROR_OBJECT_NOT_FOUND; + } + if (r == SC_SUCCESS) + r = sc_pkcs15_add_df(p15card, arr_profile_df[i].type, &path, df); + if (df) + sc_file_free(df); + + if (r != SC_SUCCESS) break; + + if (arr_profile_df[i].type == SC_PKCS15_AODF + && add_predefined_pin(p15card, &path) == SC_SUCCESS + ) + added_pin = 1; + } + if (!added_pin) + { + sc_error(ctx, "Use formating token!\n"); + sc_format_path("", &path); + r = add_predefined_pin(p15card, &path); + } + return r; +} + +int sc_pkcs15emu_rutoken_init_ex(sc_pkcs15_card_t *p15card, + sc_pkcs15emu_opt_t *opts) +{ + struct sc_card *card = p15card->card; + + SC_FUNC_CALLED(card->ctx, 1); + + /* check if we have the correct card OS */ + if (strcmp(card->name, "rutoken card")) + return SC_ERROR_WRONG_CARD; + + sc_debug(card->ctx, "%s found", card->name); + return sc_pkcs15_rutoken_init_func(p15card); +} + diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c index 0c89e82..5a73de5 100644 --- a/src/libopensc/pkcs15-syn.c +++ b/src/libopensc/pkcs15-syn.c @@ -50,6 +50,8 @@ extern int sc_pkcs15emu_atrust_acos_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts); extern int sc_pkcs15emu_tccardos_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); +extern int sc_pkcs15emu_rutoken_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); + static struct { const char * name; int (*handler)(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); @@ -65,6 +67,7 @@ static struct { { "actalis", sc_pkcs15emu_actalis_init_ex }, { "atrust-acos",sc_pkcs15emu_atrust_acos_init_ex}, { "tccardos", sc_pkcs15emu_tccardos_init_ex }, + { "rutoken", sc_pkcs15emu_rutoken_init_ex }, { NULL, NULL } }; diff --git a/src/pkcs11/framework-pkcs15.c b/src/pkcs11/framework-pkcs15.c index d133999..ab45ece 100644 --- a/src/pkcs11/framework-pkcs15.c +++ b/src/pkcs11/framework-pkcs15.c @@ -2074,6 +2074,9 @@ static CK_RV pkcs15_prkey_sign(struct sc_pkcs11_session *ses, void *obj, case CKM_RSA_X_509: flags = SC_ALGORITHM_RSA_RAW; break; + case CKM_GOST: + flags = SC_ALGORITHM_GOST; + break; default: return CKR_MECHANISM_INVALID; } @@ -2152,6 +2155,8 @@ pkcs15_prkey_decrypt(struct sc_pkcs11_session *ses, void *obj, case CKM_RSA_X_509: flags |= SC_ALGORITHM_RSA_RAW; break; + case CKM_GOST: + flags |= SC_ALGORITHM_GOST; default: return CKR_MECHANISM_INVALID; } @@ -2808,6 +2813,21 @@ static int register_mechanisms(struct sc_pkcs11_card *p11card) flags |= alg_info->flags; } + if (alg_info->algorithm == SC_ALGORITHM_GOST){ + mech_info.flags = CKF_HW | CKF_SIGN | CKF_ENCRYPT | CKF_DECRYPT; + #ifdef HAVE_OPENSSL + mech_info.flags |= CKF_VERIFY; + #endif + mech_info.ulMinKeySize = 32; + mech_info.ulMaxKeySize = 32; + mt = sc_pkcs11_new_fw_mechanism(CKM_GOST, + &mech_info, CKK_RSA, NULL); + rc = sc_pkcs11_register_mechanism(p11card, mt); + sc_debug(card->ctx, "register GOST!!! %d", rc); + if(rc < 0) + return rc; + } + alg_info++; } @@ -2865,7 +2885,7 @@ static int register_mechanisms(struct sc_pkcs11_card *p11card) return rc; #endif } - + return CKR_OK; } diff --git a/src/pkcs11/pkcs11.h b/src/pkcs11/pkcs11.h index 2e6a1e3..ee95630 100644 --- a/src/pkcs11/pkcs11.h +++ b/src/pkcs11/pkcs11.h @@ -358,6 +358,11 @@ typedef unsigned long ck_key_type_t; #define CKK_TWOFISH (0x21) #define CKK_VENDOR_DEFINED ((unsigned long) (1 << 31)) +//rutoken: +#define CKK_GOST (CKK_VENDOR_DEFINED+1) +#define CKA_GOST CKA_VENDOR_DEFINED+1 +#define CKM_GOST CKM_VENDOR_DEFINED+1 + typedef unsigned long ck_certificate_type_t; diff --git a/src/pkcs15init/Makefile.am b/src/pkcs15init/Makefile.am index c6dccc9..49ab7d3 100644 --- a/src/pkcs15init/Makefile.am +++ b/src/pkcs15init/Makefile.am @@ -20,7 +20,8 @@ PROFILES = \ setcos.profile \ pkcs15.profile \ muscle.profile \ - asepcos.profile + asepcos.profile \ + rutoken.profile EXTRA_DIST = $(PROFILES) Makefile.mak @@ -32,7 +33,7 @@ libpkcs15init_la_SOURCES = \ pkcs15-gpk.c pkcs15-miocos.c pkcs15-cflex.c \ pkcs15-cardos.c pkcs15-jcop.c pkcs15-starcos.c \ pkcs15-oberthur.c pkcs15-setcos.c pkcs15-incrypto34.c \ - pkcs15-muscle.c pkcs15-asepcos.c + pkcs15-muscle.c pkcs15-asepcos.c pkcs15-rutoken.c libpkcs15init_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ $(AM_LDFLAGS) diff --git a/src/pkcs15init/Makefile.in b/src/pkcs15init/Makefile.in index 04b838d..a266063 100644 --- a/src/pkcs15init/Makefile.in +++ b/src/pkcs15init/Makefile.in @@ -66,7 +66,7 @@ am_libpkcs15init_la_OBJECTS = pkcs15-lib.lo profile.lo keycache.lo \ pkcs15-gpk.lo pkcs15-miocos.lo pkcs15-cflex.lo \ pkcs15-cardos.lo pkcs15-jcop.lo pkcs15-starcos.lo \ pkcs15-oberthur.lo pkcs15-setcos.lo pkcs15-incrypto34.lo \ - pkcs15-muscle.lo pkcs15-asepcos.lo + pkcs15-muscle.lo pkcs15-asepcos.lo pkcs15-rutoken.lo libpkcs15init_la_OBJECTS = $(am_libpkcs15init_la_OBJECTS) DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp @@ -248,7 +248,8 @@ PROFILES = \ setcos.profile \ pkcs15.profile \ muscle.profile \ - asepcos.profile + asepcos.profile \ + rutoken.profile EXTRA_DIST = $(PROFILES) Makefile.mak lib_LTLIBRARIES = libpkcs15init.la @@ -257,7 +258,7 @@ libpkcs15init_la_SOURCES = \ pkcs15-gpk.c pkcs15-miocos.c pkcs15-cflex.c \ pkcs15-cardos.c pkcs15-jcop.c pkcs15-starcos.c \ pkcs15-oberthur.c pkcs15-setcos.c pkcs15-incrypto34.c \ - pkcs15-muscle.c pkcs15-asepcos.c + pkcs15-muscle.c pkcs15-asepcos.c pkcs15-rutoken.c libpkcs15init_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ $(AM_LDFLAGS) include_HEADERS = pkcs15-init.h @@ -345,6 +346,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-oberthur.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-setcos.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-starcos.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs15-rutoken.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/profile.Plo@am__quote@ .c.o: diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index 7f278b1..f6bfed4 100644 --- a/src/pkcs15init/pkcs15-init.h +++ b/src/pkcs15init/pkcs15-init.h @@ -401,6 +401,7 @@ extern struct sc_pkcs15init_operations *sc_pkcs15init_get_setcos_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_incrypto34_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_muscle_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_asepcos_ops(void); +extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rutoken_ops(void); #ifdef __cplusplus } diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c index abb3319..5b434ca 100644 --- a/src/pkcs15init/pkcs15-lib.c +++ b/src/pkcs15init/pkcs15-lib.c @@ -149,6 +149,7 @@ static struct profile_operations { const char *name; void *func; } profile_operations[] = { + { "rutoken", (void *) sc_pkcs15init_get_rutoken_ops }, { "gpk", (void *) sc_pkcs15init_get_gpk_ops }, { "miocos", (void *) sc_pkcs15init_get_miocos_ops }, { "flex", (void *) sc_pkcs15init_get_cryptoflex_ops }, diff --git a/src/pkcs15init/pkcs15-rutoken.c b/src/pkcs15init/pkcs15-rutoken.c new file mode 100644 index 0000000..067515d --- /dev/null +++ b/src/pkcs15init/pkcs15-rutoken.c @@ -0,0 +1,698 @@ +/* + * ruToken specific operation for PKCS15 initialization + * + * Copyright (C) 2007 Pavel Mironchik + * Copyright (C) 2007 Eugene Hermann + * + * 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 +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pkcs15-init.h" +#include "profile.h" + +#define MAX_ID 255 + +static const sc_SecAttrV2_t pr_sec_attr = {0x43, 1, 1, 0, 0, 0, 0, 1, 2, 2, 0, 0, 0, 0, 2}; +static const sc_SecAttrV2_t pb_sec_attr = {0x42, 0, 1, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 2}; +static const sc_SecAttrV2_t wn_sec_attr = {0x43, 1, 1, 0, 0, 0, 0,-1, 2, 2, 0, 0, 0, 0, 0}; +static const sc_SecAttrV2_t df_sec_attr = {0x43, 1, 1, 0, 0, 0, 0, 1, 2, 2, 0, 0, 0, 0, 2}; +static const sc_SecAttrV2_t ef_sec_attr = {0x42, 0, 1, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 2}; +static const sc_SecAttrV2_t p2_sec_attr = {0x43, 1, 1, 0, 0, 0, 0,-1, 1, 2, 0, 0, 0, 0, 0}; +static const sc_SecAttrV2_t p1_sec_attr = {0x43, 1, 1, 0, 0, 0, 0,-1, 1, 1, 0, 0, 0, 0, 0}; + +enum DF_IDs +{ + PrKDFid = 0x1001, + PuKDFid = 0x1002, + CDFid = 0x1003, + DODFid = 0x1004, + AODFid = 0xFFFF +}; + +#define PrKDF_name "PKCS15-PrKDF" +#define PuKDF_name "PKCS15-PuKDF" +#define CDF_name "PKCS15-CDF" +#define DODF_name "PKCS15-DODF" +#define AODF_name "PKCS15-AODF" +#define ODF_name "PKCS15-ODF" + +static const struct +{ + char const* name; + unsigned int dir; + unsigned int type; +} arr_def_df[] = + { + { PrKDF_name, PrKDFid, SC_PKCS15_PRKDF }, + { PuKDF_name, PuKDFid, SC_PKCS15_PUKDF }, + { CDF_name, CDFid, SC_PKCS15_CDF }, + { DODF_name, DODFid, SC_PKCS15_DODF }, + { AODF_name, AODFid, SC_PKCS15_AODF } + }; + +int get_bin_from_prkey(const struct sc_pkcs15_prkey_rsa *rsa, u8 *key, size_t *keysize); + +/* + * Create/override new EF. + */ +static int rutoken_create_file(sc_card_t *card, sc_path_t *path, sc_file_t *ef) +{ + int ret = SC_SUCCESS; + sc_path_t path_dir; + + SC_FUNC_CALLED(card->ctx, 1); + + if (path) + { + sc_ctx_suppress_errors_on(card->ctx); + ret = card->ops->select_file(card, path, NULL); + sc_ctx_suppress_errors_off(card->ctx); + if (ret == SC_SUCCESS) + { + sc_path_t del_path; + del_path.len = 2; + del_path.type = SC_PATH_TYPE_FILE_ID; + del_path.value[0] = (u8)(ef->id / 256); + del_path.value[1] = (u8)(ef->id % 256); + if (card->ops->select_file(card, &del_path, NULL) == SC_SUCCESS) + ret = card->ops->delete_file(card, &del_path); + } + path_dir = *path; + path_dir.len -= 2; + ret = card->ops->select_file(card, &path_dir, NULL); + } + if (ret == SC_SUCCESS) + { + ret = card->ops->create_file(card, ef); + } + return ret; +} + +/* + * Create a DF + */ +static int +rutoken_create_dir(sc_profile_t *profile, sc_card_t *card, sc_file_t *df) +{ + int ret; + sc_file_t *file = NULL; + + if (!card || !card->ctx || !df) + return SC_ERROR_INVALID_ARGUMENTS; + + SC_FUNC_CALLED(card->ctx, 1); + + ret = card->ops->select_file(card, &df->path, &file); + if (ret == SC_ERROR_FILE_NOT_FOUND) + ret = card->ops->create_file(card, df); + else if(file && file->type != SC_FILE_TYPE_DF) + ret = SC_ERROR_WRONG_CARD; + + if(file) + sc_file_free(file); + return ret; +} + +static int get_dfpath(sc_profile_t *profile, unsigned int df_type, + sc_path_t *out_path) +{ + static const int DFid[SC_PKCS15_DF_TYPE_COUNT] = + { PrKDFid, PuKDFid, 0, 0, CDFid, 0, 0, DODFid }; + /* assert(0 == SC_PKCS15_PRKDF); + * assert(1 == SC_PKCS15_PUKDF); + * assert(4 == SC_PKCS15_CDF); + * assert(7 == SC_PKCS15_DODF); + */ + + if (df_type >= SC_PKCS15_DF_TYPE_COUNT || !profile + || !profile->df || !profile->df[df_type] + ) + return -1; + + *out_path = profile->df[df_type]->path; + out_path->len -= 2; + sc_append_file_id(out_path, DFid[df_type]); + return 0; +} + +/* + * Select a key reference + */ +static int +rutoken_select_key_reference(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_prkey_info_t *key_info) +{ + if (!profile || !card || !card->ctx || !key_info) + return SC_ERROR_INVALID_ARGUMENTS; + + SC_FUNC_CALLED(card->ctx, 1); + + if (get_dfpath(profile, SC_PKCS15_PRKDF, &key_info->path) < 0) + { + sc_debug(card->ctx, "Call error get_dfpath\n"); + return SC_ERROR_INTERNAL; + } + sc_append_file_id(&key_info->path, key_info->key_reference); + + return key_info->key_reference >= 0 && key_info->key_reference <= MAX_ID ? + SC_SUCCESS : SC_ERROR_TOO_MANY_OBJECTS; +} + +/* + * Create a private key object. + * This is a no-op. + */ +static int +rutoken_create_key(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_object_t *obj) +{ + if (!profile || !card || !card->ctx || !obj) + return SC_ERROR_INVALID_ARGUMENTS; + SC_FUNC_CALLED(card->ctx, 1); + return SC_SUCCESS; +} + +/* + * Create private key files + */ +static int rutoken_create_prkeyfile(sc_card_t *card, + sc_pkcs15_prkey_info_t *key_info, size_t prsize) +{ + sc_path_t path; + sc_file_t *file; + int ret; + + SC_FUNC_CALLED(card->ctx, 1); + + file = sc_file_new(); + if (file) + { + /* create key file */ + path = key_info->path; + file->type = SC_FILE_TYPE_WORKING_EF; + file->id = key_info->key_reference; + file->size = prsize; + sc_file_set_sec_attr(file, (u8*)&pr_sec_attr, SEC_ATTR_SIZE); + ret = rutoken_create_file(card, &path, file); + if (file) + sc_file_free(file); + } + else + ret = SC_ERROR_OUT_OF_MEMORY; + return ret; +} + +/* + * Store a private key object. + */ +static int +rutoken_store_key(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_object_t *obj, + sc_pkcs15_prkey_t *key) +{ + sc_pkcs15_prkey_info_t *key_info; + const int nKeyBufSize = 2048; + u8 *prkeybuf = NULL; + size_t prsize; + int ret; + + if (!profile || !profile->ops || !card || !card->ctx + || !obj || !obj->data || !key + || !profile->ops->encode_private_key + ) + return SC_ERROR_INVALID_ARGUMENTS; + + SC_FUNC_CALLED(card->ctx, 1); + + if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) + return SC_ERROR_NOT_SUPPORTED; + + key_info = (sc_pkcs15_prkey_info_t *) obj->data; + prkeybuf = calloc(nKeyBufSize, 1); + if(!prkeybuf) + return SC_ERROR_OUT_OF_MEMORY; + + /* + * encode private key + * create key file + * write a key + */ + prsize = nKeyBufSize; + ret = profile->ops->encode_private_key(profile, card, &key->u.rsa, prkeybuf, &prsize, 0); + if (ret == 0) + { + ret = rutoken_create_prkeyfile(card, key_info, prsize); + if (ret == 0) + { + ret = sc_update_binary(card, 0, prkeybuf, prsize, 0); + if (ret < 0 || (size_t)ret != prsize) + sc_debug(card->ctx, "ret=%i (%u)\n", ret, prsize); + } + memset(prkeybuf, 0, prsize); + } + free(prkeybuf); + return ret; +} + +/* + * Encode private key + */ +static int +rutoken_encode_private_key(sc_profile_t *profile, sc_card_t *card, + struct sc_pkcs15_prkey_rsa *rsa, + u8 *key, size_t *keysize, int key_ref) +{ + int r; + + if (!card || !card->ctx || !rsa || !key || !keysize) + return SC_ERROR_INVALID_ARGUMENTS; + + SC_FUNC_CALLED(card->ctx, 1); + r = get_bin_from_prkey(rsa, key, keysize); + sc_debug(card->ctx, "get_bin_from_prkey returned %i\n", r); + return r; +} + +static int rutoken_id_in(int id, const u8 *buf, int buflen) +{ + int i; + for (i = 0; i*2 < buflen; i++) + if (id == (int)buf[i*2] * 0x100 + buf[i*2 + 1]) return 1; + return 0; +} + +static int rutoken_find_id(sc_card_t *card, const sc_path_t *path) +{ + int ret = SC_SUCCESS; + sc_file_t *file = NULL; + u8 *files = malloc(2048); + if (!files) return SC_ERROR_OUT_OF_MEMORY; + if(path) + { + if((ret = card->ops->select_file(card, path, &file)) == SC_SUCCESS) + ret = file->type == SC_FILE_TYPE_DF ? SC_SUCCESS : SC_ERROR_NOT_ALLOWED; + } + if(ret == SC_SUCCESS) + { + ret = card->ops->list_files(card, files, 2048); + if(ret >= 0) + { + int i; + for (i = 0; i < MAX_ID; i++) + if(!rutoken_id_in(i, files, ret)) {ret = i; break;} + } + } + free(files); + if(file) sc_file_free(file); + return ret; +} + +/* + * Create a file based on a PKCS15_TYPE_xxx + */ +static int +rutoken_new_file(struct sc_profile *profile, struct sc_card *card, + unsigned int type, unsigned int idx, struct sc_file **file) +{ + int ret = SC_SUCCESS, id, ret_s; + sc_path_t path; + u8 const *sec_attr; + + if (!profile || !file || *file != NULL + || !card || !card->ctx || !card->ops + || !card->ops->delete_file || !card->ops->select_file + || !card->ops->list_files /* for call rutoken_find_id */ + ) + return SC_ERROR_INVALID_ARGUMENTS; + + SC_FUNC_CALLED(card->ctx, 1); + + switch (type & SC_PKCS15_TYPE_CLASS_MASK) + { + case SC_PKCS15_TYPE_CERT: + ret = get_dfpath(profile, SC_PKCS15_CDF, &path); + sec_attr = pb_sec_attr; + break; + case SC_PKCS15_TYPE_PUBKEY: + ret = get_dfpath(profile, SC_PKCS15_PUKDF, &path); + sec_attr = pb_sec_attr; + break; + case SC_PKCS15_TYPE_DATA_OBJECT: + ret = get_dfpath(profile, SC_PKCS15_DODF, &path); + sec_attr = pr_sec_attr; + break; + case SC_PKCS15_TYPE_PRKEY_RSA: + default: + ret = SC_ERROR_NOT_SUPPORTED; + } + /* find first unlished file id */ + if (ret == SC_SUCCESS) + { + id = rutoken_find_id(card, &path); + if (id < 0) + { + sc_debug(card->ctx, "Error find id (%i)\n", id); + ret = SC_ERROR_TOO_MANY_OBJECTS; + } + } + if(ret == SC_SUCCESS) + { + sc_debug(card->ctx, "new id %i\n", id); + *file = sc_file_new(); + if (!*file) + ret = SC_ERROR_OUT_OF_MEMORY; + { + (*file)->size = 0; + (*file)->id = id; + sc_append_file_id(&path, (*file)->id); + (*file)->path = path; + sc_file_set_sec_attr(*file, sec_attr, SEC_ATTR_SIZE); + (*file)->type = SC_FILE_TYPE_WORKING_EF; + /* If target file exist than remove it */ + sc_ctx_suppress_errors_on(card->ctx); + ret_s = card->ops->select_file(card, &(*file)->path, NULL); + sc_ctx_suppress_errors_off(card->ctx); + if (ret_s == SC_SUCCESS) + { + sc_path_t del_path; + del_path.len = 0; + del_path.type = SC_PATH_TYPE_FILE_ID; + card->ops->delete_file(card, &del_path); + } + } + } + return ret; +} + +/* + * Initialization routine + */ + +static const struct +{ + u8 id, options, flags, try, pass[8]; + sc_SecAttrV2_t const* p_sattr; +} do_pins[] = + { + { SC_RUTOKEN_DEF_ID_GCHV_USER, SC_RUTOKEN_OPTIONS_GACCESS_USER, + SC_RUTOKEN_FLAGS_COMPACT_DO, 0xFF, + { '1', '2', '3', '4', '5', '6', '7', '8' }, &p2_sec_attr + }, + { SC_RUTOKEN_DEF_ID_GCHV_ADMIN, SC_RUTOKEN_OPTIONS_GACCESS_ADMIN, + SC_RUTOKEN_FLAGS_COMPACT_DO, 0xFF, + { '8', '7', '6', '5', '4', '3', '2', '1' }, &p1_sec_attr + } + }; + +static int create_pins(sc_card_t *card) +{ + sc_DO_V2_t param_do; + size_t i; + int r = SC_SUCCESS; + + for (i = 0; i < sizeof(do_pins)/sizeof(do_pins[0]); ++i) + { + memset(¶m_do, 0, sizeof(param_do)); + param_do.HDR.OTID.byObjectType = SC_RUTOKEN_TYPE_CHV; + param_do.HDR.OTID.byObjectID = do_pins[i].id; + param_do.HDR.OP.byObjectOptions = do_pins[i].options; + param_do.HDR.OP.byObjectFlags = do_pins[i].flags; + param_do.HDR.OP.byObjectTry = do_pins[i].try; + param_do.HDR.wDOBodyLen = sizeof(do_pins[i].pass); + /* assert(do_pins[i].p_sattr != NULL); */ + /* assert(sizeof(*param_do.HDR.SA_V2)) */ + /* assert(sizeof(param_do.HDR.SA_V2) == sizeof(*do_pins[i].p_sattr)); */ + memcpy(param_do.HDR.SA_V2, *do_pins[i].p_sattr, + sizeof(*do_pins[i].p_sattr)); + /* assert(do_pins[i].pass); */ + /* assert(sizeof(*param_do.abyDOBody)) */ + /* assert(sizeof(param_do.abyDOBody) >= sizeof(do_pins[i].pass)); */ + memcpy(param_do.abyDOBody, do_pins[i].pass, sizeof(do_pins[i].pass)); + + r = sc_card_ctl(card, SC_CARDCTL_RUTOKEN_CREATE_DO, ¶m_do); + if (r != SC_SUCCESS) break; + } + return r; +} + +static int create_typical_fs(sc_card_t *card) +{ + sc_file_t *df; + int r; + + df = sc_file_new(); + if (!df) + return SC_ERROR_OUT_OF_MEMORY; + df->type = SC_FILE_TYPE_DF; + do + { + r = sc_file_set_sec_attr(df, wn_sec_attr, SEC_ATTR_SIZE); + if (r != SC_SUCCESS) break; + + /* Create MF 3F00 */ + df->id = 0x3F00; + sc_format_path("3F00", &df->path); + r = card->ops->create_file(card, df); + if (r != SC_SUCCESS) break; + + /* Create 3F00/0000 */ + df->id = 0x0000; + sc_append_file_id(&df->path, df->id); + r = card->ops->create_file(card, df); + if (r != SC_SUCCESS) break; + + /* Create 3F00/0000/0000 */ + df->id = 0x0000; + sc_append_file_id(&df->path, df->id); + r = card->ops->create_file(card, df); + if (r != SC_SUCCESS) break; + + /* Create USER PIN and SO PIN*/ + r = create_pins(card); + if (r != SC_SUCCESS) break; + + /* VERIFY USER PIN */ + r = sc_verify(card, SC_AC_CHV, do_pins[0].id, + do_pins[0].pass, sizeof(do_pins[0].pass), NULL); + if (r != SC_SUCCESS) break; + + /* Create 3F00/0000/0000/0001 */ + df->id = 0x0001; + sc_append_file_id(&df->path, df->id); + r = card->ops->create_file(card, df); + if (r != SC_SUCCESS) break; + + sc_format_path("3F0000000000", &df->path); + r = card->ops->select_file(card, &df->path, NULL); + if (r != SC_SUCCESS) break; + + /* Create 3F00/0000/0000/0002 */ + df->id = 0x0002; + sc_append_file_id(&df->path, df->id); + r = card->ops->create_file(card, df); + if (r != SC_SUCCESS) break; + + sc_format_path("3F000000", &df->path); + r = card->ops->select_file(card, &df->path, NULL); + if (r != SC_SUCCESS) break; + + /* Create 3F00/0000/0001 */ + df->id = 0x0001; + sc_append_file_id(&df->path, df->id); + r = card->ops->create_file(card, df); + if (r != SC_SUCCESS) break; + + /* RESET ACCESS RIGHTS */ + r = sc_logout(card); + } while(0); + sc_file_free(df); + return r; +} + +/* + * Card-specific initialization of PKCS15 profile information + */ +static int rutoken_init(sc_profile_t *profile, sc_card_t *card) +{ + struct file_info *ef_list; + sc_file_t *df, *ef; + size_t i; + int r, ret = SC_SUCCESS; + + SC_FUNC_CALLED(card->ctx, 1); + + df = sc_file_new(); + if (!df) + { + sc_debug(card->ctx, "Failed to create file\n"); + return SC_ERROR_OUT_OF_MEMORY; + } + df->type = SC_FILE_TYPE_DF; + r = sc_file_set_sec_attr(df, df_sec_attr, SEC_ATTR_SIZE); + if (r != SC_SUCCESS) + sc_debug(card->ctx, "Failed to set secure attr: %s\n", sc_strerror(r)); + + for (ef_list = profile->ef_list; ef_list; ef_list = ef_list->next) + { + if (!ef_list->file || ef_list->file->path.len <= 2) + continue; + df->path = ef_list->file->path; + df->path.len -= 2; + ret = card->ops->select_file(card, &df->path, NULL); + if (ret != SC_SUCCESS) + { + sc_debug(card->ctx,"Failed select file: %s\n", sc_strerror(ret)); + break; + } + + sc_file_dup(&ef, ef_list->file); + if (!ef) + { + sc_debug(card->ctx, "Failed to dup file\n"); + ret = SC_ERROR_OUT_OF_MEMORY; + break; + } + r = sc_file_set_sec_attr(ef, + ef->type == SC_FILE_TYPE_DF ? + df_sec_attr : ef_sec_attr, SEC_ATTR_SIZE); + if (r != SC_SUCCESS) + sc_debug(card->ctx, "Failed to set secure attr: %s\n", + sc_strerror(r)); + + ret = card->ops->create_file(card, ef); + sc_file_free(ef); + if (ret != SC_SUCCESS) + { + sc_error(card->ctx, "Failed to create file " + "in compliance with profile: %s\n", + sc_strerror(ret)); + break; + } + + for (i = 0; i < sizeof(arr_def_df)/sizeof(arr_def_df[0]); ++i) + if (arr_def_df[i].dir != AODFid + && strcasecmp(ef_list->ident, arr_def_df[i].name) == 0 + ) + { + df->id = arr_def_df[i].dir; + r = sc_append_file_id(&df->path, df->id); + if (r == SC_SUCCESS) + r = card->ops->create_file(card, df); + if (r != SC_SUCCESS) + sc_error(card->ctx, "Failed to create df, %s\n", + sc_strerror(r)); + break; + } + } + sc_file_free(df); + return ret; +} + +/* + * Erase everything that's on the card + * And create PKCS15 profile + */ +static int +rutoken_erase(struct sc_profile *profile, sc_card_t *card) +{ + int ret, ret_end; + + if (!profile || !card || !card->ctx || !card->ops + || !card->ops->select_file || !card->ops->create_file + ) + return SC_ERROR_INVALID_ARGUMENTS; + + SC_FUNC_CALLED(card->ctx, 1); + + /* ret = sc_card_ctl(card, SC_CARDCTL_ERASE_CARD, NULL); */ + ret = sc_card_ctl(card, SC_CARDCTL_RUTOKEN_FORMAT_INIT, NULL); + if (ret != SC_SUCCESS) + sc_error(card->ctx, "Failed to erase: %s\n", sc_strerror(ret)); + else + { + ret = create_typical_fs(card); + if (ret != SC_SUCCESS) + sc_error(card->ctx, "Failed to create typical fs: %s\n", + sc_strerror(ret)); + ret_end = sc_card_ctl(card, SC_CARDCTL_RUTOKEN_FORMAT_END, NULL); + if (ret_end != SC_SUCCESS) + ret = ret_end; + if (ret == SC_SUCCESS) + { + /* VERIFY __default__ USER PIN */ + /* assert(sizeof(do_pins)/sizeof(do_pins[0]) >= 1); */ + /* assert(do_pins[0].id == SC_RUTOKEN_DEF_ID_GCHV_USER); */ + ret = sc_verify(card, SC_AC_CHV, do_pins[0].id, + do_pins[0].pass, sizeof(do_pins[0].pass), NULL); + if (ret != SC_SUCCESS) + sc_debug(card->ctx, "VERIFY default USER PIN: %s\n", + sc_strerror(ret)); + else + { + ret = rutoken_init(profile, card); + + /* RESET ACCESS RIGHTS */ + if (sc_logout(card) != SC_SUCCESS) + sc_debug(card->ctx, + "Failed RESET ACCESS RIGHTS\n"); + } + } + if (ret != SC_SUCCESS) + sc_error(card->ctx, "Failed to init PKCS15: %s\n", + sc_strerror(ret)); + } + return ret; +} + +static struct sc_pkcs15init_operations sc_pkcs15init_rutoken_operations = { + rutoken_erase, /* erase_card */ + NULL, /* init_card */ + rutoken_create_dir, /* create_dir */ + NULL, /* create_domain */ + NULL, /* select_pin_reference */ + NULL, /* create_pin */ + rutoken_select_key_reference, /* select_key_reference */ + rutoken_create_key, /* create_key */ + rutoken_store_key, /* store_key */ + NULL, /* generate_key */ + rutoken_encode_private_key, /* encode_private_key */ + NULL, /* encode_public_key */ + NULL, /* finalize_card */ + /* Old-style API */ + NULL, /* init_app */ + NULL, /* new_pin */ + NULL, /* new_key */ + rutoken_new_file, /* new_file */ + NULL, /* old_generate_key */ + NULL /* delete_object */ +}; + +struct sc_pkcs15init_operations* sc_pkcs15init_get_rutoken_ops(void) +{ + return &sc_pkcs15init_rutoken_operations; +} + diff --git a/src/pkcs15init/rutoken.profile b/src/pkcs15init/rutoken.profile new file mode 100644 index 0000000..605b49a --- /dev/null +++ b/src/pkcs15init/rutoken.profile @@ -0,0 +1,89 @@ +# +# PKCS15 profile, generic information. +# This profile is loaded before any card specific profile. +# + +# +# The following controls some aspects of the PKCS15 we put onto +# the card. +# +pkcs15 { + # Put certificates into the CDF itself? + direct-certificates = no; + # Put the DF length into the ODF file? + encode-df-length = no; + # Have a lastUpdate field in the EF(TokenInfo)? + do-last-update = yes; +} + +# Default settings. +# This option block will always be processed. +option default_32k { + macros { + odf-size = 0; + aodf-size = 0; + dodf-size = 2048; + cdf-size = 2048; + prkdf-size = 2048; + pukdf-size = 2048; + } +} + +# This option is for cards with very little memory. +# It sets the size of various PKCS15 directory files +# to 128 or 256, respectively. +#option small { +option default { + macros { + odf-size = 0; + aodf-size = 0; + dodf-size = 512; + cdf-size = 512; + prkdf-size = 512; + pukdf-size = 512; + } +} + +filesystem { + DF MF { + path = 3F00; + type = DF; + + # Here comes the application DF + DF PKCS15-AppDF { + type = DF; + file-id = FF00; + + EF PKCS15-ODF { + file-id = 00DF; + size = $odf-size; + } + + EF PKCS15-AODF { + file-id = A0DF; + size = $aodf-size; + } + + EF PKCS15-PrKDF { + file-id = 0001; + size = $prkdf-size; + } + + EF PKCS15-PuKDF { + file-id = 0002; + size = $pukdf-size; + } + + EF PKCS15-CDF { + file-id = 0003; + size = $cdf-size; + } + + EF PKCS15-DODF { + file-id = 0004; + size = $dodf-size; + } + } + } +} + diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index e5f6248..b0a7966 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -12,11 +12,12 @@ PROGRAMS_OPENSSL = cryptoflex-tool pkcs15-init netkey-tool piv-tool endif bin_PROGRAMS = opensc-tool opensc-explorer pkcs15-tool pkcs15-crypt \ - pkcs11-tool cardos-info eidenv \ + pkcs11-tool cardos-info eidenv rutoken-tool \ $(PROGRAMS_OPENSSL) opensc_tool_SOURCES = opensc-tool.c util.c $(top_srcdir)/src/common/my_getopt.c piv_tool_SOURCES = piv-tool.c util.c $(top_srcdir)/src/common/my_getopt.c +piv_tool_LDADD = $(OPENSSL_LIBS) opensc_explorer_SOURCES = opensc-explorer.c util.c $(top_srcdir)/src/common/my_getopt.c opensc_explorer_LDADD = $(LIBREADLINE) pkcs15_tool_SOURCES = pkcs15-tool.c util.c $(top_srcdir)/src/common/my_getopt.c @@ -33,5 +34,7 @@ cardos_info_SOURCES = cardos-info.c util.c $(top_srcdir)/src/common/my_getopt.c eidenv_SOURCES = eidenv.c $(top_srcdir)/src/common/my_getopt.c netkey_tool_SOURCES = netkey-tool.c $(top_srcdir)/src/common/my_getopt.c netkey_tool_LDADD = $(OPENSSL_LIBS) +rutoken_tool_SOURCES = rutoken-tool.c util.c +rutoken_tool_LDADD = $(OPENSSL_LIBS) noinst_HEADERS = util.h diff --git a/src/tools/Makefile.in b/src/tools/Makefile.in index c712578..dcb58b0 100644 --- a/src/tools/Makefile.in +++ b/src/tools/Makefile.in @@ -42,6 +42,7 @@ host_triplet = @host@ bin_PROGRAMS = opensc-tool$(EXEEXT) opensc-explorer$(EXEEXT) \ pkcs15-tool$(EXEEXT) pkcs15-crypt$(EXEEXT) \ pkcs11-tool$(EXEEXT) cardos-info$(EXEEXT) eidenv$(EXEEXT) \ + rutoken-tool$(EXEEXT) \ $(am__EXEEXT_1) subdir = src/tools DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ @@ -64,6 +65,10 @@ am_cardos_info_OBJECTS = cardos-info.$(OBJEXT) util.$(OBJEXT) \ my_getopt.$(OBJEXT) cardos_info_OBJECTS = $(am_cardos_info_OBJECTS) cardos_info_LDADD = $(LDADD) +am_rutoken_tool_OBJECTS = rutoken-tool.$(OBJEXT) util.$(OBJEXT) \ + my_getopt.$(OBJEXT) +rutoken_tool_OBJECTS = $(am_rutoken_tool_OBJECTS) +rutoken_tool_LDADD = $(LDADD) am_cryptoflex_tool_OBJECTS = cryptoflex-tool.$(OBJEXT) util.$(OBJEXT) \ my_getopt.$(OBJEXT) cryptoflex_tool_OBJECTS = $(am_cryptoflex_tool_OBJECTS) @@ -121,13 +126,13 @@ SOURCES = $(cardos_info_SOURCES) $(cryptoflex_tool_SOURCES) \ $(opensc_explorer_SOURCES) $(opensc_tool_SOURCES) \ $(piv_tool_SOURCES) $(pkcs11_tool_SOURCES) \ $(pkcs15_crypt_SOURCES) $(pkcs15_init_SOURCES) \ - $(pkcs15_tool_SOURCES) + $(pkcs15_tool_SOURCES) $(rutoken_tool_SOURCES) DIST_SOURCES = $(cardos_info_SOURCES) $(cryptoflex_tool_SOURCES) \ $(eidenv_SOURCES) $(netkey_tool_SOURCES) \ $(opensc_explorer_SOURCES) $(opensc_tool_SOURCES) \ $(piv_tool_SOURCES) $(pkcs11_tool_SOURCES) \ $(pkcs15_crypt_SOURCES) $(pkcs15_init_SOURCES) \ - $(pkcs15_tool_SOURCES) + $(pkcs15_tool_SOURCES) $(rutoken_tool_SOURCES) HEADERS = $(noinst_HEADERS) ETAGS = etags CTAGS = ctags @@ -283,6 +288,7 @@ AM_LDFLAGS = @LIBOPENSC@ @HAVE_OPENSSL_TRUE@PROGRAMS_OPENSSL = cryptoflex-tool pkcs15-init netkey-tool piv-tool opensc_tool_SOURCES = opensc-tool.c util.c $(top_srcdir)/src/common/my_getopt.c piv_tool_SOURCES = piv-tool.c util.c $(top_srcdir)/src/common/my_getopt.c +piv_tool_LDADD = $(OPENSSL_LIBS) opensc_explorer_SOURCES = opensc-explorer.c util.c $(top_srcdir)/src/common/my_getopt.c opensc_explorer_LDADD = $(LIBREADLINE) pkcs15_tool_SOURCES = pkcs15-tool.c util.c $(top_srcdir)/src/common/my_getopt.c @@ -296,6 +302,7 @@ cryptoflex_tool_LDADD = $(OPENSSL_LIBS) pkcs15_init_SOURCES = pkcs15-init.c util.c $(top_srcdir)/src/common/my_getopt.c pkcs15_init_LDADD = $(OPENSSL_LIBS) ../pkcs15init/libpkcs15init.la cardos_info_SOURCES = cardos-info.c util.c $(top_srcdir)/src/common/my_getopt.c +rutoken_tool_SOURCES = rutoken-tool.c util.c $(top_srcdir)/src/common/my_getopt.c eidenv_SOURCES = eidenv.c $(top_srcdir)/src/common/my_getopt.c netkey_tool_SOURCES = netkey-tool.c $(top_srcdir)/src/common/my_getopt.c netkey_tool_LDADD = $(OPENSSL_LIBS) @@ -364,6 +371,9 @@ clean-binPROGRAMS: cardos-info$(EXEEXT): $(cardos_info_OBJECTS) $(cardos_info_DEPENDENCIES) @rm -f cardos-info$(EXEEXT) $(LINK) $(cardos_info_LDFLAGS) $(cardos_info_OBJECTS) $(cardos_info_LDADD) $(LIBS) +rutoken-tool$(EXEEXT): $(rutoken_tool_OBJECTS) $(rutoken_tool_DEPENDENCIES) + @rm -f rutoken-tool$(EXEEXT) + $(LINK) $(rutoken_tool_LDFLAGS) $(rutoken_tool_OBJECTS) $(rutoken_tool_LDADD) $(LIBS) cryptoflex-tool$(EXEEXT): $(cryptoflex_tool_OBJECTS) $(cryptoflex_tool_DEPENDENCIES) @rm -f cryptoflex-tool$(EXEEXT) $(LINK) $(cryptoflex_tool_LDFLAGS) $(cryptoflex_tool_OBJECTS) $(cryptoflex_tool_LDADD) $(LIBS) @@ -402,6 +412,7 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cardos-info.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rutoken-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cryptoflex-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eidenv.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/my_getopt.Po@am__quote@ diff --git a/src/tools/pkcs11-tool.c b/src/tools/pkcs11-tool.c index 2178e14..6a1684f 100644 --- a/src/tools/pkcs11-tool.c +++ b/src/tools/pkcs11-tool.c @@ -3567,6 +3567,7 @@ static struct mech_info p11_mechanisms[] = { { CKM_DSA_PARAMETER_GEN, "DSA-PARAMETER-GEN", NULL }, { CKM_DH_PKCS_PARAMETER_GEN,"DH-PKCS-PARAMETER-GEN", NULL }, { CKM_X9_42_DH_PARAMETER_GEN,"X9-42-DH-PARAMETER-GEN", NULL }, + { CKM_GOST,"GOST", NULL }, { NO_MECHANISM, NULL, NULL } }; diff --git a/src/tools/rutoken-tool.c b/src/tools/rutoken-tool.c new file mode 100644 index 0000000..46ebc38 --- /dev/null +++ b/src/tools/rutoken-tool.c @@ -0,0 +1,537 @@ +/* + * rutoken-tool.c: RuToken Tool + * + * Copyright (C) 2007 Pavel Mironchik + * Copyright (C) 2007 Eugene Hermann + * + * 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 +#endif +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include "util.h" + +#define IV_SIZE 8 +#define HASH_SIZE 4 + +static const char *app_name = "rutoken-tool"; + +enum { + OP_NONE, + OP_GET_INFO, + OP_GEN_KEY, + OP_ENCRYPT, + OP_DECRYPT, + OP_MAC +}; + +static const struct option options[] = { + {"reader", 1, NULL, 'r'}, + {"wait", 0, NULL, 'w'}, + {"pin", 1, NULL, 'p'}, + {"key", 1, NULL, 'k'}, + {"IV", 1, NULL, 'I'}, + {"type", 1, NULL, 't'}, + {"input", 1, NULL, 'i'}, + {"output", 1, NULL, 'o'}, + {"info", 0, NULL, 's'}, + {"genkey", 0, NULL, 'g'}, + {"encrypt", 0, NULL, 'e'}, + {"decrypt", 0, NULL, 'd'}, + {"mac", 0, NULL, 'm'}, + {"verbose", 0, NULL, 'v'}, + {NULL, 0, NULL, 0 } +}; + +static const char *option_help[] = { + "Uses reader number [0]", + "Wait for a card to be inserted", + "Specify PIN", + "Selects the GOST key ID to use", + "Initialization vector of the encryption to use", + "Specify a new GOST key type: ECB (default), SM or CFB", + "Selects the input file to cipher", + "Selects the output file to cipher", + "Show ruToken information", + "Generate new GOST key", + "Performs GOST encryption operation", + "Performs GOST decryption operation", + "Performs MAC computation with GOST key", + "Verbose operation. Use several times to enable debug output." +}; + +/* Get ruToken device information */ + +static int rutoken_info(sc_card_t *card) +{ + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + sc_serial_number_t serial; + int r; + + r = sc_card_ctl(card, SC_CARDCTL_RUTOKEN_GET_INFO, rbuf); + if (r) { + fprintf(stderr, "Error: Get info failed: %s\n", sc_strerror(r)); + return -1; + } + printf("Type: %d\n", rbuf[0]); + printf("Version: %d.%d\n", rbuf[1]>>4, rbuf[1] & 0x0F); + printf("Memory: %d Kb\n", rbuf[2]*8); + printf("Protocol version: %d\n", rbuf[3]); + printf("Software version: %d\n", rbuf[4]); + printf("Order: %d\n", rbuf[5]); + + r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); + if (r) { + fprintf(stderr, "Error: Get serial failed: %s\n", sc_strerror(r)); + return -1; + } + printf("Serial number: "); + hex_dump(stdout, serial.value, serial.len, NULL); + putchar('\n'); + return 0; +} + +/* Cipher/Decipher a buffer on token (used GOST key chosen by ID) */ + +static int rutoken_cipher(sc_card_t *card, u8 keyid, + const u8 *in, size_t inlen, + u8 *out, size_t outlen, int oper) +{ + int r; + struct sc_rutoken_decipherinfo inf = { in, inlen, out, outlen }; + sc_security_env_t env; + int cmd = (oper == OP_ENCRYPT) ? + SC_CARDCTL_RUTOKEN_GOST_ENCIPHER : + SC_CARDCTL_RUTOKEN_GOST_DECIPHER; + + memset(&env, 0, sizeof(env)); + env.key_ref[0] = keyid; + env.key_ref_len = 1; + env.algorithm = SC_ALGORITHM_GOST; + env.operation = SC_SEC_OPERATION_DECIPHER; + + /* set security env */ + r = sc_set_security_env(card, &env, 0); + if (r) { + fprintf(stderr, "Error: Cipher failed (set security environment): %s\n", + sc_strerror(r)); + return -1; + } + /* cipher */ + r = sc_card_ctl(card, cmd, &inf); + if (r) { + fprintf(stderr, "Error: Cipher failed: %s\n", sc_strerror(r)); + return -1; + } + return 0; +} + +/* Compute MAC a buffer on token (used GOST key chosen by ID) */ + +static int rutoken_mac(sc_card_t *card, u8 keyid, + const u8 *in, size_t inlen, + u8 *out, size_t outlen) +{ + int r; + sc_security_env_t env; + + memset(&env, 0, sizeof(env)); + env.key_ref[0] = keyid; + env.key_ref_len = 1; + env.algorithm = SC_ALGORITHM_GOST; + env.operation = SC_SEC_OPERATION_SIGN; + + /* set security env */ + r = sc_set_security_env(card, &env, 0); + if (r) { + fprintf(stderr, "Error: Computation signature (MAC) failed" + " (set security environment): %s\n", sc_strerror(r)); + return -1; + } + /* calculate hash */ + r = sc_compute_signature(card, in, inlen, out, outlen); + if (r) { + fprintf(stderr, "Error: Computation signature (MAC) failed: %s\n", + sc_strerror(r)); + return -1; + } + return 0; +} + +/* Encrypt/Decrupt infile to outfile */ + +static int do_crypt(sc_card_t *card, u8 keyid, + const char *path_infile, const char *path_outfile, + const u8 IV[IV_SIZE], int oper) +{ + int err; + int fd_in, fd_out; + struct stat st; + size_t insize, outsize, readsize; + u8 *inbuf = NULL, *outbuf = NULL, *p; + + fd_in = open(path_infile, O_RDONLY); + if (fd_in < 0) { + fprintf(stderr, "Error: Cannot open file '%s'\n", path_infile); + return -1; + } + err = fstat(fd_in, &st); + if (err || (oper == OP_DECRYPT && st.st_size < IV_SIZE)) { + fprintf(stderr, "Error: File '%s' is invalid\n", path_infile); + close(fd_in); + return -1; + } + insize = st.st_size; + if (oper == OP_ENCRYPT) + insize += IV_SIZE; + outsize = insize; + if (oper == OP_DECRYPT) /* !(stat.st_size < IV_SIZE) already true */ + outsize -= IV_SIZE; + + inbuf = malloc(insize); + outbuf = malloc(outsize); + if (!inbuf || !outbuf) { + fprintf(stderr, "Error: File '%s' is too big (allocate memory)\n", + path_infile); + err = -1; + } + if (err == 0) { + p = inbuf; + readsize = insize; + if (oper == OP_ENCRYPT) { + memcpy(inbuf, IV, IV_SIZE); /* Set IV in first bytes buf */ + /* insize >= IV_SIZE already true */ + p += IV_SIZE; + readsize -= IV_SIZE; + } + err = read(fd_in, p, readsize); + if (err < 0 || (size_t)err != readsize) { + fprintf(stderr, "Error: Read file '%s' failed\n", path_infile); + err = -1; + } + else + err = 0; + } + close(fd_in); + + if (err == 0) { + fd_out = open(path_outfile, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); + if (fd_out < 0) { + fprintf(stderr, "Error: Cannot create file '%s'\n",path_outfile); + err = -1; + } + else { + err = rutoken_cipher(card, keyid, inbuf, insize, + outbuf, outsize, oper); + if (err == 0) { + err = write(fd_out, outbuf, outsize); + if (err < 0 || (size_t)err != outsize) { + fprintf(stderr,"Error: Write file '%s' failed\n", + path_outfile); + err = -1; + } + else + err = 0; + } + close(fd_out); + } + } + if (outbuf) + free(outbuf); + if (inbuf) + free(inbuf); + return err; +} + +/* Cipher/Decipher + (for cipher IV is parameters or random generated on token) */ + +static int gostchiper(sc_card_t *card, u8 keyid, + const char *path_infile, const char *path_outfile, + const char IV[IV_SIZE], int is_iv, int op_oper) +{ + int r; + u8 iv[IV_SIZE]; + + if (op_oper == OP_ENCRYPT) { + if (!is_iv) { + /* generated random on token */ + r = sc_get_challenge(card, iv, IV_SIZE); + if (r) { + fprintf(stderr, "Error: Generate IV" + " (get challenge) failed: %s\n", + sc_strerror(r)); + return -1; + } + } + else + memcpy(iv, IV, IV_SIZE); + } + return do_crypt(card, keyid, path_infile, path_outfile, iv, op_oper); +} + +/* Print MAC infile (used GOST key chosen by ID) */ + +static int gostmac(sc_card_t *card, u8 keyid, const char *path_infile) +{ + int err; + int fd; + struct stat st; + size_t insize; + u8 *inbuf = NULL; + u8 outbuf[HASH_SIZE]; + + fd = open(path_infile, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Error: Cannot open file '%s'\n", path_infile); + return -1; + } + err = fstat(fd, &st); + if (err) { + fprintf(stderr, "Error: File '%s' is invalid\n", path_infile); + close(fd); + return -1; + } + insize = st.st_size; + inbuf = malloc(insize); + if (!inbuf) { + fprintf(stderr, "Error: File '%s' is too big (allocate memory)\n", + path_infile); + err = -1; + } + if (err == 0) { + err = read(fd, inbuf, insize); + if (err < 0 || (size_t)err != insize) { + fprintf(stderr, "Error: Read file '%s' failed\n", path_infile); + err = -1; + } + else + err = rutoken_mac(card, keyid, inbuf, insize, + outbuf, sizeof(outbuf)); + } + if (err == 0) { + hex_dump(stdout, outbuf, sizeof(outbuf), NULL); + putchar('\n'); + } + if (inbuf) + free(inbuf); + close(fd); + return err; +} + +/* Generate GOST key on ruToken card */ + +static int generate_gostkey(sc_card_t *card, u8 keyid, u8 keyoptions) +{ + const sc_SecAttrV2_t gk_sec_attr = + {0x44, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 2}; + sc_DOHdrV2_t paramkey; + int r; + + memset(¶mkey, 0, sizeof(paramkey)); + paramkey.wDOBodyLen = SC_RUTOKEN_DEF_LEN_DO_GOST; + paramkey.OTID.byObjectType = SC_RUTOKEN_TYPE_KEY; + paramkey.OTID.byObjectID = keyid; + paramkey.OP.byObjectOptions = keyoptions; + + /* assert(sizeof(*gk_sec_attr)); */ + /* assert(sizeof(*paramkey.SA_V2)); */ + /* assert(sizeof(paramkey.SA_V2) == sizeof(gk_sec_attr)); */ + memcpy(paramkey.SA_V2, gk_sec_attr, sizeof(gk_sec_attr)); + + r = sc_card_ctl(card, SC_CARDCTL_RUTOKEN_GENERATE_KEY_DO, ¶mkey); + if (r) { + fprintf(stderr, "Error: Generate GOST key failed: %s\n", sc_strerror(r)); + return -1; + } + return 0; +} + +int main(int argc, char* argv[]) +{ + int opt_reader = -1; + int opt_wait = 0; + const char *opt_pin = NULL; + int opt_key = 0; + int opt_is_iv = 0; + u8 opt_keytype = SC_RUTOKEN_OPTIONS_GOST_CRYPT_PZ; + const char *opt_input = NULL; + const char *opt_output = NULL; + int opt_operation = OP_NONE; + int opt_debug = 0; + char IV[IV_SIZE]; + + int err = 0; + sc_context_t *ctx = NULL; + sc_context_param_t ctx_param; + sc_card_t *card = NULL; + int c, long_optind, r, tries_left; + + while (1) { + c = getopt_long(argc, argv, "r:wp:k:I:t:i:o:sgedmv", + options, &long_optind); + if (c == -1) + break; + switch (c) { + case '?': + print_usage_and_die(app_name, options, option_help); + case 'r': + opt_reader = atoi(optarg); + break; + case 'w': + opt_wait = 1; + break; + case 'p': + opt_pin = optarg; + break; + case 'k': + opt_key = atoi(optarg); + if (opt_key <= 0 || opt_key < SC_RUTOKEN_DO_ALL_MIN_ID + || opt_key > SC_RUTOKEN_DO_NOCHV_MAX_ID) { + fprintf(stderr, "Error: Key ID is invalid" + " (%d <= ID <= %d)\n", + SC_RUTOKEN_DO_ALL_MIN_ID > 0 ? + SC_RUTOKEN_DO_ALL_MIN_ID : 1, + SC_RUTOKEN_DO_NOCHV_MAX_ID); + return -1; + } + break; + case 'I': + opt_is_iv = 1; + strncpy(IV, optarg, sizeof(IV)); + break; + case 't': + if (strcmp(optarg, "CFB") == 0) + opt_keytype = SC_RUTOKEN_OPTIONS_GOST_CRYPT_GAMMOS; + else if (strcmp(optarg, "SM") == 0) + opt_keytype = SC_RUTOKEN_OPTIONS_GOST_CRYPT_GAMM; + else if (strcmp(optarg, "ECB") != 0) { + fprintf(stderr, "Error: Key type must be either" + " ECB, SM or CFB\n"); + return -1; + } + break; + case 'i': + opt_input = optarg; + break; + case 'o': + opt_output = optarg; + break; + case 's': + opt_operation = OP_GET_INFO; + break; + case 'g': + opt_operation = OP_GEN_KEY; + break; + case 'e': + opt_operation = OP_ENCRYPT; + break; + case 'd': + opt_operation = OP_DECRYPT; + break; + case 'm': + opt_operation = OP_MAC; + break; + case 'v': + opt_debug++; + break; + } + } + + memset(&ctx_param, 0, sizeof(ctx_param)); + ctx_param.app_name = app_name; + r = sc_context_create(&ctx, &ctx_param); + if (r) { + fprintf(stderr, "Error: Failed to establish context: %s\n", + sc_strerror(r)); + return -1; + } + ctx->debug = opt_debug; + + if (connect_card(ctx, &card, opt_reader, 0, opt_wait, opt_debug) != 0) + err = -1; + + if (err == 0 && opt_pin) { + /* verify */ + r = sc_verify(card, SC_AC_CHV, SC_RUTOKEN_DEF_ID_GCHV_USER, + (u8*)opt_pin, strlen(opt_pin), &tries_left); + if (r) { + fprintf(stderr, "Error: PIN verification failed: %s", + sc_strerror(r)); + if (r == SC_ERROR_PIN_CODE_INCORRECT) + fprintf(stderr, " (tries left %d)\n", tries_left); + else + putc('\n', stderr); + err = 1; + } + } + if (err == 0) { + err = -1; + switch (opt_operation) { + case OP_GET_INFO: + err = rutoken_info(card); + break; + case OP_DECRYPT: + case OP_ENCRYPT: + case OP_MAC: + if (!opt_input) { + fprintf(stderr, "Error: No input file specified\n"); + break; + } + if (opt_operation != OP_MAC && !opt_output) { + fprintf(stderr, "Error: No output file specified\n"); + break; + } + case OP_GEN_KEY: + if (opt_key == 0) { + fprintf(stderr, "Error: You must set key ID\n"); + break; + } + if (opt_operation == OP_GEN_KEY) + err = generate_gostkey(card, (u8)opt_key, opt_keytype); + else if (opt_operation == OP_MAC) + err = gostmac(card, (u8)opt_key, opt_input); + else + err = gostchiper(card, (u8)opt_key, opt_input,opt_output, + IV, opt_is_iv, opt_operation); + break; + default: + fprintf(stderr, "Error: No operation specified\n"); + break; + } + } + if (card) { + /* sc_lock and sc_connect_card in connect_card */ + sc_unlock(card); + sc_disconnect_card(card, 0); + } + if (ctx) + sc_release_context(ctx); + return err; +} +