From 0ecc294ed8bea00c712e90b0db3ea29eef935730 Mon Sep 17 00:00:00 2001 From: "ludovic.rousseau" Date: Mon, 17 Dec 2007 13:39:20 +0000 Subject: [PATCH] add support of ruToken Thanks to Andrew V. Stepanov for the patch http://www.opensc-project.org/pipermail/opensc-devel/2007-December/010617.html git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3304 c6295689-39f2-0310-b995-f0e70906c6a9 --- etc/opensc.conf.in | 2 +- src/libopensc/Makefile.am | 4 +- src/libopensc/card-rutoken.c | 1325 ++++++++++++++++++++++++++++++++ src/libopensc/cardctl.h | 134 +++- src/libopensc/ctx.c | 1 + src/libopensc/opensc.h | 7 + src/libopensc/pkcs15-algo.c | 6 + src/libopensc/pkcs15-rutoken.c | 892 +++++++++++++++++++++ src/libopensc/pkcs15-syn.c | 3 + src/pkcs11/framework-pkcs15.c | 20 + src/pkcs11/pkcs11.h | 4 + src/pkcs15init/Makefile.am | 1 + src/pkcs15init/pkcs15-init.h | 1 + src/pkcs15init/pkcs15-lib.c | 1 + src/pkcs15init/rutoken.profile | 76 ++ src/tools/Makefile.am | 4 +- src/tools/pkcs11-tool.c | 1 + src/tools/rutoken-tool.c | 473 ++++++++++++ 18 files changed, 2947 insertions(+), 8 deletions(-) create mode 100644 src/libopensc/card-rutoken.c create mode 100644 src/libopensc/pkcs15-rutoken.c create mode 100644 src/pkcs15init/rutoken.profile create mode 100644 src/tools/rutoken-tool.c diff --git a/etc/opensc.conf.in b/etc/opensc.conf.in index 876536a605..d32947e520 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, gemsafeGPK, gemsafeV1, tccardos, PIV-II; + builtin_emulators = esteid, openpgp, tcos, starcert, infocamere, postecert, actalis, atrust-acos, gemsafeGPK, gemsafeV1, tccardos, PIV-II, rutoken; # additional settings per driver # diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index b9ebe65654..71e1dfc239 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -31,12 +31,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-gemsafeV1.c \ + card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c\ \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \ pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \ - compression.c p15card-helper.c + compression.c p15card-helper.c pkcs15-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) diff --git a/src/libopensc/card-rutoken.c b/src/libopensc/card-rutoken.c new file mode 100644 index 0000000000..ee88442bb6 --- /dev/null +++ b/src/libopensc/card-rutoken.c @@ -0,0 +1,1325 @@ +/* + * 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 "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*/ + +int get_prkey_from_bin(u8* data, int len, struct sc_pkcs15_prkey **key); + +#ifdef BIG_ENDIAN_RUTOKEN +#define MF_PATH "\x3F\x00" +#else +#define MF_PATH "\x00\x3F" +#endif +struct auth_senv { + unsigned int algorithm; + int key_file_id; + size_t key_size; + unsigned int algorithm_flags; + sc_path_t path; +}; +typedef struct auth_senv auth_senv_t; + +static const sc_SecAttrV2_t default_sec_attr = { + 0x40, + 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 2 +}; + +static const struct sc_card_operations *iso_ops = NULL; + +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 } +}; + +static int make_le_path(u8 *hPath, size_t len); +static int rutoken_get_do_info(sc_card_t *card, sc_DO_INFO_t * pInfo); + +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; +#ifdef DEBUG + /* if(!card->ctx->debug) card->ctx->debug = 1; */ +#endif + 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); +} + +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; +} + + +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; +} + +/* 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 +} + +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) */ + /* *((unsigned short int*)(out) + 1) = (unsigned short int)file->size; */ +#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 */ + /* *((unsigned short int*)(out) + 3) = (unsigned short int)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_logout(sc_card_t *card) +{ + sc_apdu_t apdu; + int ret = SC_ERROR_CARD_CMD_FAILED; + sc_path_t path; + + 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_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); +} + +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) + { + const char PRK_DF[] = "3F0000000000FF001001"; + sc_debug(card->ctx, "RSA\n"); + senv->algorithm = SC_ALGORITHM_RSA_RAW; + if(env->operation == SC_SEC_OPERATION_DECIPHER || env->operation == SC_SEC_OPERATION_SIGN) + { + sc_format_path(PRK_DF, &senv->path); + sc_append_path(&senv->path, &env->file_ref); + } + else ret = SC_ERROR_INVALID_ARGUMENTS; + 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 (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; + } + SC_FUNC_RETURN(card->ctx, 1, ret); +} + +void rutoken_set_do_hdr(u8 *data, sc_DOHdrV2_t *pHdr) +{ + if(data) + { + data[0] = (u8)(pHdr->wDOBodyLen % 0x100); + data[1] = (u8)(pHdr->wDOBodyLen / 0x100); + 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); + } +} + +void rutoken_set_do(u8 *data, sc_DO_V2_t * pDO) +{ + rutoken_set_do_hdr(data, &pDO->HDR); + memcpy(data + SC_RUTOKEN_DO_HDR_LEN, pDO->abyDOBody, pDO->HDR.wDOBodyLen); +} + +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(data, pDO); + + 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) +{ + sc_apdu_t apdu; + int rv = SC_NO_ERROR; + + sc_debug(card->ctx,": crgram_len %i; outlen %i\n", crgram_len, outlen); + if (!out || !outlen) + return SC_ERROR_INVALID_ARGUMENTS; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, p1, p2); + + apdu.resp = (u8*)malloc(SC_MAX_APDU_BUFFER_SIZE); + if (!apdu.resp) + return SC_ERROR_OUT_OF_MEMORY; + apdu.resplen = SC_MAX_APDU_BUFFER_SIZE; + if (crgram_len < 16 || ((crgram_len) % 8)) + rv = SC_ERROR_WRONG_LENGTH; + size_t cur_len = 0; + unsigned char is_first = 1; + unsigned int cur_data_len; + + while( (rv == SC_NO_ERROR) && (cur_len != crgram_len) && (outlen > cur_len)) + { + cur_data_len = (crgram_len - cur_len) % 248; + if(!cur_data_len) cur_data_len = 248; + + apdu.data = crgram + cur_len; + apdu.lc = apdu.datalen = cur_data_len; + apdu.cla = crgram_len - cur_len - cur_data_len > 0 ? 0x10 : 0x00; + apdu.le = apdu.resplen = 248; + + if((rv = sc_transmit_apdu(card, &apdu)) >= 0 && + (rv = rutoken_check_sw(card, apdu.sw1, apdu.sw2)) == SC_NO_ERROR) + { + if(isIV && is_first) + { + /* break initialization vector */ + memcpy(out, apdu.resp + 8, apdu.resplen - 8); + out += apdu.resplen - 8; + cur_len += apdu.resplen; + is_first = 0; + } + else + { + /* memcpy(out + cur_len, apdu.resp, apdu.resplen); */ + memcpy(out, apdu.resp, apdu.resplen); + cur_len += apdu.resplen; + out += apdu.resplen; + } + } + } + if (rv == SC_NO_ERROR) rv = (cur_len == crgram_len) ? isIV ? cur_len - 8 : cur_len : SC_ERROR_BUFFER_TOO_SMALL; + + if (apdu.resp) + free(apdu.resp); + + sc_debug(card->ctx, "return decipher len %d\n", rv); + return rv; +} + +/* Launcher for chipher */ +static int rutoken_decipher(sc_card_t *card, struct sc_rutoken_decipherinfo *ptr) +{ + return rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, ptr->outbuf, ptr->outlen, 0x80, 0x86, 1); +} + +/* Launcher for chipher */ + +static int rutoken_encipher(sc_card_t *card, struct sc_rutoken_decipherinfo *ptr) +{ + return rutoken_cipher_p(card, ptr->inbuf, ptr->inlen, ptr->outbuf, ptr->outlen, 0x86, 0x80, 0); +} + +int rutoken_read_file(sc_card_t *card, sc_path_t *path, u8 **out, int *len) +{ + int r; + u8 *data = NULL; + sc_file_t *file = NULL; + r = sc_lock(card); + SC_TEST_RET(card->ctx, r, "sc_lock() failed"); + *len = 0; + r = rutoken_select_file(card, path, &file); + if (r == SC_SUCCESS) + { + data = (u8 *) malloc(file->size); + if (data == NULL) + r = SC_ERROR_OUT_OF_MEMORY; + } + if (r == SC_SUCCESS) + r = sc_read_binary(card, 0, data, file->size, 0); + if (file && r == file->size) + { + *len = r; + *out = data; + r = SC_SUCCESS; + } + else + free(data); + sc_unlock(card); + + if(file) sc_file_free(file); + + return r; +} + +int rutoken_read_prkey(sc_card_t *card, + sc_path_t *path, + struct sc_pkcs15_prkey **out) +{ + int ret, len = 0; + u8 *data = NULL; + + ret = rutoken_read_file(card, path, &data, &len); + if (ret == SC_SUCCESS) + { + ret = get_prkey_from_bin(data, len, out); + } + + return ret; +} + +/* RSA emulation */ + +#ifdef HAVE_OPENSSL +#define GETBN(bn) ((bn)->len? BN_bin2bn((bn)->data, (bn)->len, NULL) : NULL) +static int extract_key(sc_card_t *card, sc_path_t *path, EVP_PKEY **pk) +{ + struct sc_pkcs15_prkey *key = NULL; + int r; + + r = rutoken_read_prkey(card, path, &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, sc_path_t *path, + const u8 *data, size_t len, u8 *out, size_t out_len) +{ + auth_senv_t *senv = (auth_senv_t *)card->drv_data; + EVP_PKEY *pkey = NULL; + int ret; + + out_len = 0; + ret = extract_key(card, path, &pkey); + if (!senv) ret = SC_ERROR_INTERNAL; + if (ret >= 0) + { + switch (senv->algorithm) + { + case SC_ALGORITHM_RSA_RAW: + ret = RSA_private_encrypt(len, data, out, pkey->pkey.rsa, + RSA_PKCS1_PADDING); + if ( ret >= 0) + ret = out_len; + else + { + 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(); + } + break; + } + } + if(pkey)EVP_PKEY_free(pkey); + return ret; +} + +#if 0 +static int decipher_ext(sc_card_t *card, sc_path_t *path, + const u8 *data, size_t len, u8 *out, size_t out_len) +{ + //int r = SC_ERROR_NOT_SUPPORTED; + auth_senv_t *senv = (auth_senv_t *)card->drv_data; + EVP_PKEY *pkey = NULL; + + int r; + + out_len = 0; + r = extract_key(card, path, &pkey); + if (r < 0) + return r; + + switch (senv->algorithm) + { + case SC_ALGORITHM_RSA_RAW: + r = RSA_private_decrypt(len, data, out, pkey->pkey.rsa, + RSA_NO_PADDING); + if ( r >= 0) + out_len = r; + else + { + r = 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(); + } + break; + } + if(pkey)EVP_PKEY_free(pkey); + /* + EVP_PKEY_RSA *pkey = NULL; + + r = extract_key(card, &senv->path, &pkey); + if (r < 0) + return r; + + switch (senv->algorithm) { + case SC_ALGORITHM_RSA_RAW: + r = EVP_PKEY_decrypt(out, data, len, pkey); + if (r <= 0) { + fprintf(stderr, "Decryption failed.\n"); + r = 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(); + } + break; + default: + fprintf(stderr, "Key type not supported.\n"); + r = SC_ERROR_NOT_SUPPORTED; + } + + if(pkey)EVP_PKEY_free(pkey);*/ + return r; +} +#endif + +static int rutoken_decipher_rsa(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) +{ +#if 0 + //TODO: Soft RSA encryption. Uncomment and check that; + auth_senv_t *senv = (auth_senv_t *)card->drv_data; + 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) + { + int key_id = senv->key_file_id; + return decipher_ext(card, &senv->path, data, datalen, out, outlen); + } + else + return SC_ERROR_NOT_SUPPORTED; +#endif + return SC_ERROR_NOT_SUPPORTED; +} + +static int rutoken_compute_signature_gost(sc_card_t *card, const u8 *in, size_t ilen, u8 * out, size_t olen) +{ + sc_debug(card->ctx, "sign gost"); + int ret = SC_ERROR_CARD_CMD_FAILED; + sc_apdu_t apdu = {0}; + u8 *buff[256] = {0}; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x90, 0x80); + apdu.lc = apdu.datalen = ilen; + apdu.data = in; + apdu.resplen = olen; + apdu.resp = (u8*)buff; + apdu.le = 4; + if(sc_transmit_apdu(card, &apdu) >= 0) + { + memcpy(out, apdu.resp, apdu.resplen); + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + } + SC_FUNC_RETURN(card->ctx, 4, apdu.resplen); +} + + +static int rutoken_compute_signature(struct sc_card *card, const u8 * data, size_t datalen, + u8 * out, size_t outlen) +{ + sc_debug(card->ctx, "sign"); + auth_senv_t *senv = (auth_senv_t *)card->drv_data; + if(senv->algorithm == SC_ALGORITHM_GOST) + { + return rutoken_compute_signature_gost(card, data, datalen, out, outlen); + } + else if (senv->algorithm == SC_ALGORITHM_RSA_RAW) + { + return sign_ext(card, &senv->path, data, datalen, out, outlen); + } + return SC_ERROR_NOT_SUPPORTED; +} +#endif + + + +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_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_tries_left(sc_card_t *card, int *chv_tries_left) +{ + int ret = SC_ERROR_INCORRECT_PARAMETERS; + sc_apdu_t apdu; + if (*chv_tries_left != 1 && *chv_tries_left != 2 && *chv_tries_left != 0) + SC_FUNC_RETURN(card->ctx, 1, ret); + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, *chv_tries_left); + if((ret = sc_transmit_apdu(card, &apdu)) >= 0) + { + ret = rutoken_check_sw(card, apdu.sw1, apdu.sw2); + if (ret == SC_ERROR_PIN_CODE_INCORRECT) + *chv_tries_left = (int)(apdu.sw2 & 0x0f); + } + 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 ? 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_encipher(card, ptr); + break; + case SC_CARDCTL_RUTOKEN_GOST_DECIPHER: + ret = rutoken_decipher(card, ptr); + break; + case SC_CARDCTL_RUTOKEN_TRIES_LEFT: + ret = rutoken_tries_left(card, ptr); + 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.verify = rutoken_verify; */ + + #ifdef HAVE_OPENSSL + rutoken_ops.decipher = rutoken_decipher_rsa; + 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.logout = rutoken_logout; + + 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 2b73605a1e..a81beecc07 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -139,7 +139,23 @@ 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 specific 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_TRIES_LEFT }; enum { @@ -357,9 +373,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 +439,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{ + 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 2283ae3c7e..756c867c8f 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 a444411e75..4c3c0fdeec 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 @@ -1158,6 +1164,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 1a766c1834..ea546e6cb4 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-rutoken.c b/src/libopensc/pkcs15-rutoken.c new file mode 100644 index 0000000000..86b81e2813 --- /dev/null +++ b/src/libopensc/pkcs15-rutoken.c @@ -0,0 +1,892 @@ +/* + * 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 "../pkcs15init/pkcs15-init.h" +#include "../pkcs15init/profile.h" + +#define USAGE_AUT SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ +SC_PKCS15_PRKEY_USAGE_DECRYPT | \ +SC_PKCS15_PRKEY_USAGE_WRAP | \ +SC_PKCS15_PRKEY_USAGE_UNWRAP | \ +SC_PKCS15_PRKEY_USAGE_SIGN + +#define P15_DF(T) T & SC_PKCS15_TYPE_CERT ? SC_PKCS15_CDF \ + : T & SC_PKCS15_TYPE_PUBKEY ? SC_PKCS15_PUKDF : \ + T & SC_PKCS15_TYPE_PRKEY_RSA ? SC_PKCS15_PRKDF : SC_PKCS15_DODF + +int rutoken_erase(struct sc_profile *, sc_card_t *); + +#define MAX_ID 255 + +const sc_SecAttrV2_t map_sec_attr = {0x42, 0, 1, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 2}; +const sc_SecAttrV2_t pr_sec_attr = {0x43, 1, 1, 0, 0, 0, 0, 1, 2, 2, 0, 0, 0, 0, 2}; +const sc_SecAttrV2_t pb_sec_attr = {0x42, 0, 1, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 2}; + +const char GCHV_DF[] = "3F0000000000"; +const char APP_DF[] = "3F0000000000FF00"; +const char PRK_DF[] = "3F0000000000FF001001"; +const char PUK_DF[] = "3F0000000000FF001002"; +const char C_DF[] = "3F0000000000FF001003"; + +enum DF_IDs +{ + PrKDFid = 0x1001, + PuKDFid = 0x1002, + CDFid = 0x1003, + DFsId = 0xefff, + DFsSize = 2048 +}; + +/* BLOB definition */ + +typedef struct _RSAPUBKEY { + int magic; + int bitlen; + int 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; + +void ArrayReverse(u8 *buf, int size); +int bin_to_privite_blob(PRIVATEKEYBLOB *pr_blob, u8 *buf, int buf_len); + +/* BLOB */ + +int create_privite_blob(PRIVATEKEYBLOB *pr_blob, const struct sc_pkcs15_prkey_rsa *key){ + int bitlen = key->modulus.len*8; + /* 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; + int n = key->exponent.len; + while (n > 0) + { + ((u8*)&pr_blob->rsapubkey.pubexp)[key->exponent.len - n] = key->exponent.data[n - 1]; + 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); + + + 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; +} + +int get_sc_pksc15_prkey_rsa(const PRIVATEKEYBLOB *pr_blob, struct sc_pkcs15_prkey_rsa *key){ + int bitlen = pr_blob->rsapubkey.bitlen; + long exp = 0x00010001; + 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 - 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(3); + memcpy(key->exponent.data, &exp, 3); + key->exponent.len = 3; + + 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; +} + +int get_privite_blob_len(const PRIVATEKEYBLOB *pr_blob){ + return sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + 9*(pr_blob->rsapubkey.bitlen/16); +} + +int free_privite_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; +} + + +int privite_blob_to_bin(const PRIVATEKEYBLOB *pr_blob, u8 *buf, size_t *buf_len){ + + if(*buf_len < get_privite_blob_len(pr_blob) + 2) + 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); + + 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 = get_privite_blob_len(pr_blob) + 2; + return 0; +} + +int get_prkey_from_bin(u8* data, int len, struct sc_pkcs15_prkey **key) +{ + int ret = -1; + *key = malloc(sizeof(struct sc_pkcs15_prkey)); + if(data && *key) + { + PRIVATEKEYBLOB pr_blob; + memset(*key, 0, sizeof(struct sc_pkcs15_prkey)); + bin_to_privite_blob(&pr_blob, data, len); + ret = get_sc_pksc15_prkey_rsa(&pr_blob, &(*key)->u.rsa); + (*key)->algorithm = SC_ALGORITHM_RSA; + free_privite_blob(&pr_blob); + } + return ret; +} + +void ArrayReverse(u8 *buf, int size) +{ + int i, j; + u8 *tmp = malloc(size); + if (tmp) + { + for(i = 0, j = size - 1; i < size; i++, j--) + tmp[i] = buf[j]; + memcpy(buf, tmp, size); + free(tmp); + } +} + +int bin_to_privite_blob(PRIVATEKEYBLOB *pr_blob, u8 *buf, int buf_len){ + + u8 *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); + + int bitlen = pr_blob->rsapubkey.bitlen; + 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); + + memcpy(pr_blob->modulus, tmp, pr_blob->rsapubkey.bitlen/8); + tmp += pr_blob->rsapubkey.bitlen/8; + + memcpy(pr_blob->prime1, tmp, pr_blob->rsapubkey.bitlen/16); + tmp += pr_blob->rsapubkey.bitlen/16; + + memcpy(pr_blob->prime2, tmp, pr_blob->rsapubkey.bitlen/16); + tmp += pr_blob->rsapubkey.bitlen/16; + + memcpy(pr_blob->exponent1, tmp, pr_blob->rsapubkey.bitlen/16); + tmp += pr_blob->rsapubkey.bitlen/16; + + memcpy(pr_blob->exponent2, tmp, pr_blob->rsapubkey.bitlen/16); + tmp += pr_blob->rsapubkey.bitlen/16; + + memcpy(pr_blob->coefficient, tmp, pr_blob->rsapubkey.bitlen/16); + tmp += pr_blob->rsapubkey.bitlen/16; + + memcpy(pr_blob->privateExponent, tmp, pr_blob->rsapubkey.bitlen/8); + tmp += pr_blob->rsapubkey.bitlen/8; + + return 0; +} + +/* + * Create/override new EF. + */ +int rutoken_create_file(sc_card_t *card, sc_path_t *path, sc_file_t *ef) +{ + int ret = SC_SUCCESS; + if(path) + { + ret = card->ops->select_file(card, path, NULL); + 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); + } + } + 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_SUCCESS; + sc_file_t *file = NULL; + + 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; +} + + +/* + * 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) +{ + SC_FUNC_CALLED(card->ctx, 1); + /* hocus-pocus :) */ + sc_format_path(PRK_DF, &key_info->path); + sc_append_file_id(&key_info->path, key_info->key_reference); + //g_nKeyRef = 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) +{ + SC_FUNC_CALLED(card->ctx, 1); + return 0; +} + +/* Send the pkcs15 profile to its doom. NOTE:This change current DF! */ +static void fix_pkcs15(sc_profile_t *profile, sc_card_t *card, int type, int x) +{ + sc_file_t *df; + + if(profile->df[type]) + df = profile->df[type]; + else + { + df = sc_file_new(); + profile->df[type] = df; + } + + switch(type) + { + case SC_PKCS15_PRKDF: + sc_format_path(PRK_DF, &df->path); + break; + case SC_PKCS15_PUKDF: + sc_format_path(PUK_DF, &df->path); + break; + case SC_PKCS15_CDF: + sc_format_path(C_DF, &df->path); + break; + default: + return; + } + df->id = DFsId; + df->size = DFsSize; + df->type = SC_FILE_TYPE_WORKING_EF; + df->ef_structure = SC_FILE_EF_TRANSPARENT; + + sc_append_file_id(&df->path, df->id); + sc_path_t odf_path; + sc_format_path("3f0000000000dfff", &odf_path); + if(card->ops->select_file(card, &odf_path, NULL) == SC_SUCCESS) + { + odf_path.len = 0; + odf_path.type = SC_PATH_TYPE_FILE_ID; + card->ops->delete_file(card, &odf_path); + } +} + +int rutoken_check_df(sc_card_t *card, int df_id) +{ + int ret = -1; + sc_path_t path; + + sc_file_t *file = sc_file_new(); + if(file) + { + sc_format_path(GCHV_DF, &path); + ret = card->ops->select_file(card, &path, NULL); + } + else + ret = SC_ERROR_OUT_OF_MEMORY; + if (ret == SC_SUCCESS) + { + sc_format_path(APP_DF, &file->path); + file->type = SC_FILE_TYPE_DF; + // FIXME: change to 'df' secattr + sc_file_set_sec_attr(file, (u8*)&pb_sec_attr, SEC_ATTR_SIZE); + /* appdf (ff00) */ + file->id = 0xff00; + ret = rutoken_create_dir(NULL, card, file); + } + if (ret == SC_SUCCESS) + { + /* p15df */ + sc_format_path(APP_DF, &path); + card->ops->select_file(card, &path, NULL); + file->id = df_id; + file->path = path; + sc_append_file_id(&file->path, df_id); + ret = rutoken_create_dir(NULL, card, file); + } + if(file) sc_file_free(file); + return ret; +} + +/* + * create private key files + */ +int rutoken_create_prkeyfile(sc_card_t *card, + sc_pkcs15_prkey_info_t *key_info, + sc_file_t **prkf, size_t prsize) +{ + int ret; + SC_FUNC_CALLED(card->ctx, 1); + sc_path_t path; + int id = key_info->key_reference; + { + sc_file_t *file = sc_file_new(); + if (file) ret = rutoken_check_df(card, PrKDFid); + else ret = SC_ERROR_OUT_OF_MEMORY; + if (ret == SC_SUCCESS) + { + /* create key file */ + sc_format_path(PRK_DF, &path); + file->type = SC_FILE_TYPE_WORKING_EF; + file->id = id; + 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); + } + 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_FUNC_CALLED(card->ctx, 1); + sc_pkcs15_prkey_info_t *key_info = (sc_pkcs15_prkey_info_t *) obj->data; + const int nKeyBufSize = 2048; + u8 *prkeybuf = NULL; + size_t prsize; + int ret; + if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) + return SC_ERROR_NOT_SUPPORTED; + + prkeybuf = calloc(nKeyBufSize, 1); + if(!prkeybuf) + return SC_ERROR_OUT_OF_MEMORY; + + /* encode private key + * create key file + * write a key */ + prsize = nKeyBufSize; + + if((ret = profile->ops->encode_private_key(profile, card, &key->u.rsa, prkeybuf, &prsize, 0)) == 0 && + ( ret = rutoken_create_prkeyfile(card, key_info, NULL, prsize)) == 0) + { + if((ret = sc_update_binary(card, 0, prkeybuf, prsize, 0)) == prsize) + { + fix_pkcs15(profile, card, P15_DF(obj->type), 0); + } + } + free(prkeybuf); + return ret; +} +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) +{ + PRIVATEKEYBLOB prkeyblob; + create_privite_blob(&prkeyblob, rsa); + int r = privite_blob_to_bin(&prkeyblob, key, keysize); + free_privite_blob(&prkeyblob); + return r; +} + + + +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; +} + +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; +} + +int rutoken_new_file(struct sc_profile *profile, struct sc_card *card, + unsigned int type, unsigned int idx, struct sc_file **file) +{ + SC_FUNC_CALLED(card->ctx, 1); + int ret = SC_SUCCESS, id; + sc_path_t path; + switch (type & SC_PKCS15_TYPE_CLASS_MASK) + { + case SC_PKCS15_TYPE_CERT: + + ret = rutoken_check_df(card, CDFid); + /* find first unlished file id */ + if (ret == SC_SUCCESS) ret = (id = rutoken_find_id(card, NULL)) >= 0 ? SC_SUCCESS : SC_ERROR_TOO_MANY_OBJECTS; + sc_format_path(C_DF, &path); + break; + case SC_PKCS15_TYPE_PUBKEY: + ret = rutoken_check_df(card, PuKDFid); + if (ret == SC_SUCCESS) ret = (id = rutoken_find_id(card, NULL)) >= 0 ? SC_SUCCESS : SC_ERROR_TOO_MANY_OBJECTS; + sc_format_path(PUK_DF, &path); + break; + case SC_PKCS15_TYPE_PRKEY_RSA: + default: + ret = SC_ERROR_NOT_SUPPORTED; + } + + if(ret == SC_SUCCESS) + { + *file = sc_file_new(); + (*file)->size = 0; + (*file)->id = id; + sc_append_file_id(&path, (*file)->id); + (*file)->path = path; + sc_file_set_sec_attr(*file, (u8*)&pb_sec_attr, SEC_ATTR_SIZE); + (*file)->type = SC_FILE_TYPE_WORKING_EF; + /* If target file exist than remove it */ + if (card->ops->select_file(card, &(*file)->path, NULL) == 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); + } + fix_pkcs15(profile, card, P15_DF(type), 0); + } + return ret; +} + +int rutoken_delete_object(struct sc_profile *profile, struct sc_card *card, + unsigned int type, const void *data, const sc_path_t *path) +{ + int ret = -1, tries_left = 2; + /* try to logon as user*/ + card->ops->logout(card); + + u8 *pin; + ret = card->ops->card_ctl(card, SC_CARDCTL_RUTOKEN_TRIES_LEFT, &tries_left); + while(ret == SC_ERROR_PIN_CODE_INCORRECT && tries_left > 0) + { + pin = (u8*)getpass("Please enter User PIN: "); + ret = sc_verify(card, SC_AC_CHV, 2, pin, 8, NULL); + if(ret != SC_SUCCESS) + { + tries_left = 2; + card->ops->card_ctl(card, SC_CARDCTL_RUTOKEN_TRIES_LEFT, &tries_left); + fprintf(stderr, "PIN code verification failed: %s\n%d tries left\n", sc_strerror(ret), tries_left); + } + } + if( (ret == SC_SUCCESS) && + (ret = card->ops->select_file(card, path, NULL) == SC_SUCCESS)) + { + sc_path_t del_path; + memset(&del_path, 0, sizeof(del_path)); + del_path.type = SC_PATH_TYPE_FILE_ID; + del_path.value[0] = path->value[path->len - 2]; + del_path.value[1] = path->value[path->len - 1]; + del_path.len = 2; + + ret = sc_delete_file(card, &del_path); + } + return ret; +} + + +/* +* Inicialization routine +*/ + +/* Complete initialization */ +int rutoken_check_sw(sc_card_t *, unsigned int, unsigned int); +int rutoken_finalize_card(sc_card_t *card) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + SC_FUNC_CALLED(card->ctx, 1); + sc_apdu_t apdu = {0}; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x7b, 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); +} + +/* Try to delete pkcs15 structure */ +int rutoken_erase(struct sc_profile *profile, sc_card_t *card) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + SC_FUNC_CALLED(card->ctx, 1); + sc_apdu_t apdu = {0}; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x7a, 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); + + return ret; +} + +/* create pkcs15 structure */ +int rutoken_init(sc_profile_t *profile, sc_card_t *card) +{ + return SC_ERROR_NOT_SUPPORTED; +} + + +static struct sc_pkcs15init_operations sc_pkcs15init_rutoken_operations = { + rutoken_erase, + rutoken_init, /* init_card */ + rutoken_create_dir, + NULL, /* create_domain */ + NULL/*rutoken_select_pin_reference*/, + NULL/*rutoken_create_pin*/, + rutoken_select_key_reference, + rutoken_create_key, + rutoken_store_key, + NULL, /* rutoken_generate_key, */ + rutoken_encode_private_key, + NULL, /* encode private/public key */ + rutoken_finalize_card, /* finalize_card */ + + NULL, NULL, NULL, + rutoken_new_file, + NULL, /* old style api */ + + rutoken_delete_object /* delete_object */ +}; + +struct sc_pkcs15init_operations * +sc_pkcs15init_get_rutoken_ops(void) +{ + return &sc_pkcs15init_rutoken_operations; +} + +static void set_string(char **strp, const char *value) +{ + if (*strp) free(*strp); + *strp = value ? strdup(value) : NULL; +} + +#define KEY_LABEL "GOST 28.147-89 KEY" +#define OBJ_LABEL "SE object" +#define RUT_LABEL "ruToken card" + +static const struct { + int type, id, auth_id, min_length; + unsigned char reference; + const char *path; + const char *label; + int flags; +} pinlist[]= +{ + + {1, 2, 2, 1, 0x02, "3f0000000000", "User PIN", + SC_PKCS15_PIN_FLAG_LOCAL|SC_PKCS15_PIN_FLAG_CASE_SENSITIVE /*| SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN*/}, + {0, 0, 0, 0, 0, NULL, NULL, 0} +}; + +int sc_pkcs15emu_rutoken_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts); + +void add_predefined_pin(sc_pkcs15_card_t *p15card) +{ + int i; + sc_path_t path; + sc_pkcs15_pin_info_t *pin_info = (sc_pkcs15_pin_info_t *) calloc(1, sizeof(*pin_info)); + sc_pkcs15_object_t *pin_obj = (sc_pkcs15_object_t *) calloc(1, sizeof(*pin_obj)); + for(i=0; pinlist[i].id; ++i) + { + sc_format_path(pinlist[i].path, &path); + pin_info->auth_id.len = 1; + pin_info->auth_id.value[0] = 1; + 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 = pinlist[i].min_length; + pin_info->stored_length = 16; + pin_info->max_length = 16; + pin_info->pad_char = -1; + pin_info->tries_left = 1; + sc_format_path(pinlist[i].path, &pin_info->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); + } +} + +static int sc_pkcs15_rutoken_init_func(sc_pkcs15_card_t *p15card) +{ + int ret = SC_ERROR_WRONG_CARD; + sc_card_t *card = p15card->card; + sc_context_t *ctx = p15card->card->ctx; + sc_path_t path; + sc_file_t *odf; + 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"); + goto failed; + } + sc_bin_to_hex(serialnr.value, serialnr.len , serial, sizeof(serial), 0); + set_string(&p15card->serial_number, serial); + /* ct_debug("serial_number = %s", serial); */ + + /* get ruToken information */ + if (sc_card_ctl(card, SC_CARDCTL_RUTOKEN_GET_INFO, info) < 0) + { + sc_debug(ctx, "unable to get token information"); + goto failed; + } + 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); + + odf = sc_file_new(); + if(odf) + { + sc_format_path("3f0000000000dfff", &odf->path); + odf->id = 0xdfff; + odf->type = SC_FILE_TYPE_WORKING_EF; + odf->ef_structure = SC_FILE_EF_TRANSPARENT; + odf->size = 1024; + p15card->file_odf = odf; + } + + add_predefined_pin(p15card); + + while (p15card->df_list) + sc_pkcs15_remove_df(p15card, p15card->df_list); + + + sc_file_t *df = sc_file_new(); + if (df) + { + df->id = DFsId; + df->size = 0; + df->type = SC_FILE_TYPE_WORKING_EF; + df->ef_structure = SC_FILE_EF_TRANSPARENT; + + + sc_format_path(PRK_DF, &path); + sc_append_file_id(&path, df->id); + if(card->ops->select_file(card, &path, NULL) == SC_SUCCESS) + { + df->path = path; + sc_pkcs15_add_df(p15card, SC_PKCS15_PRKDF, &path, df); + } + sc_format_path(PUK_DF, &path); + sc_append_file_id(&path, df->id); + if(card->ops->select_file(card, &path, NULL) == SC_SUCCESS) + { + df->path = path; + sc_pkcs15_add_df(p15card, SC_PKCS15_PUKDF, &path, df); + } + sc_format_path(C_DF, &path); + sc_append_file_id(&path, df->id); + if(card->ops->select_file(card, &path, NULL) == SC_SUCCESS) + { + df->path = path; + sc_pkcs15_add_df(p15card, SC_PKCS15_CDF, &path, df); + } + sc_file_free(df); + } + ret = SC_SUCCESS; +failed: + return ret; +} + +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 1877b9e162..0cdd8020e3 100644 --- a/src/libopensc/pkcs15-syn.c +++ b/src/libopensc/pkcs15-syn.c @@ -52,6 +52,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 *); @@ -68,6 +70,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 b393db8455..afdc5f4205 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++; } diff --git a/src/pkcs11/pkcs11.h b/src/pkcs11/pkcs11.h index 2e6a1e3ed3..8c62a567f7 100644 --- a/src/pkcs11/pkcs11.h +++ b/src/pkcs11/pkcs11.h @@ -358,6 +358,10 @@ 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 c6dccc9893..f04bb764e3 100644 --- a/src/pkcs15init/Makefile.am +++ b/src/pkcs15init/Makefile.am @@ -20,6 +20,7 @@ PROFILES = \ setcos.profile \ pkcs15.profile \ muscle.profile \ + rutoken.profile \ asepcos.profile diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index 7f278b18e3..f6bfed4cee 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 abb3319dbb..5b434cac77 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/rutoken.profile b/src/pkcs15init/rutoken.profile new file mode 100644 index 0000000000..7f369e05d3 --- /dev/null +++ b/src/pkcs15init/rutoken.profile @@ -0,0 +1,76 @@ + +cardinfo { + max-pin-length = 8; + pin-encoding = ascii-numeric; + pin-pad-char = 0x00; +} +option default { + macros { + so-pin-flags = initialized, needs-padding, soPin; + isf_acl = WRITE=$SOPIN; + df_acl = *=$SOPIN; + } +} + + +# Define reasonable limits for PINs and PUK +# We set the reference for SO pin+puk here, because +# those are hard-coded (if a PUK us assigned). +PIN so-pin { + reference = 0; +} +PIN so-puk { + reference = 1; +} +PIN user-pin { + attempts = 15; +} +PIN user-puk { + attempts = 15; +} + +# Additional filesystem info. +# This is added to the file system info specified in the +# main profile. + +filesystem { + DF MF { + DF { + type = DF; + file-id = 0000; + acl = *=NONE; + DF { + type = DF; + file-id = 0000; + acl = *=NONE; + + DF { + type = DF; + file-id = 0000; + acl = *=NONE; + } + + DF { + type = DF; + file-id = 0001; + acl = *=NONE; + } + + DF { + type = DF; + file-id = 0002; + acl = *=NONE; + } + + } + DF { + type = DF; + file-id = 0001; + acl = *=NONE; + } + + + } + } +} + diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index e5f6248d69..8aec093d80 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -12,7 +12,7 @@ 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 @@ -33,5 +33,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/pkcs11-tool.c b/src/tools/pkcs11-tool.c index 2178e14cd8..6a1684f9d9 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 0000000000..42e4b50ed6 --- /dev/null +++ b/src/tools/rutoken-tool.c @@ -0,0 +1,473 @@ +/* + * 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 +#include "util.h" + +//#define _DEBUG +#ifdef _DEBUG +#define trace printf("%s, %s line %d: ", __FUNCTION__, __FILE__, __LINE__); printf +#else +#define trace +#endif + + +/* globals */ +const char *app_name = "rutoken-tool"; +sc_context_t *g_ctx = NULL; + +enum { + OP_NONE, + OP_GET_INFO, + OP_ENCIPHER, + OP_DECIPHER, + OP_SIGN, + OP_FORMAT +}; + +enum { + OPT_BASE = 0x100, + OPT_PIN, + OPT_SOPIN +}; + +const struct option options[] = { + {"reader", 1, 0, 'r'}, + {"card-driver", 1, 0, 'c'}, + {"wait", 0, 0, 'w'}, + {"verbose", 0, 0, 'v'}, + {"getinfo", 0, 0, 'g'}, + {"encrypt", 0, 0, 'e'}, + {"decrypt", 0, 0, 'u'}, + {"sign", 0, 0, 's'}, + {"key", 1, 0, 'k'}, + {"i-vector",1, 0, 'I'}, + {"pin", 1, 0, OPT_PIN}, + {"so-pin", 1, 0, OPT_SOPIN}, + {"input", 1, 0, 'i'}, + {"output", 1, 0, 'o'}, + {"format", 0, 0, 'F'}, + {0, 0, 0, 0} +}; + +const char *option_help[] = { + "Uses reader number [0]", + "Forces the use of driver [auto-detect]", + "Wait for a card to be inserted", + "Verbose operation. Use several times to enable debug output.", + "Get RuToken info", + "GOST encrypt", + "GOST decrypt", + "sign", + "use GOST key", + "use initialization vector (synchro posylka)", + "user pin", + "admin pin", + "input file path", + "output file path", + "format card" +}; + + +/* Get ruToken device information */ +int rutoken_info(sc_card_t *card) +{ + trace("enter\n"); + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + char szInfo[SC_MAX_APDU_BUFFER_SIZE*4]; + int r; + + + r = card->ops->card_ctl(card, SC_CARDCTL_RUTOKEN_GET_INFO, rbuf); + if (r) { + fprintf(stderr, "get info failed: %s\n", + sc_strerror(r)); + return 1; + } + sc_bin_to_hex(rbuf, 8, szInfo, sizeof(szInfo), 0); + + printf("Type: %d\n", *((char *)rbuf)); + printf("Version: %d.%d\n", (*((char *)rbuf+1))>>4, (*((char *)rbuf+1))&0x0F ); + printf("Memory: %d Kb\n", *((char *)rbuf+2)*8); + printf("Protocol version: %d\n", *((char *)rbuf+3)); + printf("Software version: %d\n", *((char *)rbuf+4)); + printf("Order: %d\n", *((char *)rbuf+5)); + + sc_serial_number_t serial; + r = card->ops->card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial); + if (r) { + fprintf(stderr, "get serial failed: %s\n", + sc_strerror(r)); + return 1; + } + sc_bin_to_hex(serial.value, serial.len , szInfo, sizeof(szInfo), 0); + printf("Serial number : %s\n", szInfo); + return 0; +} + +/* Cryptografic routine */ + +/* Size of file */ + +off_t get_file_size(int fd) +{ + off_t cur_pos; + off_t file_size; + cur_pos = lseek(fd, 0, SEEK_CUR); + file_size = lseek(fd, 0, SEEK_END); + lseek(fd, cur_pos, SEEK_SET); + return file_size; +} + +/* Allocate buffer and read file, insert initialization vector if needIV + return buffer size */ + +int get_file(sc_card_t *card, const char *filepath, u8 **ppBuf, int needIV, u8 *IV) +{ + int file = open(filepath, O_RDONLY); + int ret = -1, size = -1; + + if(file > 0) + { + size = get_file_size(file); + trace("size = %d\n", size); + if(size > 0) *ppBuf = realloc(*ppBuf, needIV ? size + 8 : size); + if(*ppBuf) + { + trace("needIV %d, %p\n", needIV, IV); + if (needIV) + { + if (IV) + ret = memcpy(*ppBuf, IV, 8) != *ppBuf; + else + { + trace("get_challenge\n"); + ret = card->ops->get_challenge(card, *ppBuf, 8); + } + if (ret == SC_SUCCESS) + { + ret = read(file, *ppBuf + 8, size) + 8; + size += 8; + } + else + ret = -1; + } + else + ret = read(file, *ppBuf, size); + } + trace("ret = %d, size = %d\n", ret, size); + if( ret != size) + { + printf("Read error!!!\n"); + free(*ppBuf); + ret = -1; + } + close(file); + } + else + printf("File %s not found\n", filepath); + return ret; +} + +/* Write buffer to a file + sync = NULL if not sync decrypt */ + +int write_file(const char *filepath, u8 *buff, size_t len) +{ + int file, r; + file = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH); + if( file < 0 ) { + printf("File %s not found\n", filepath); + return -1; + } + r = write(file, buff, len); + if( r < 0) { + printf("Write error!!!\n"); + return r; + }; + return r; +} + +/* Decrypt a buffer */ + +int rutoken_decipher(sc_card_t *card, u8 keyid, u8 *in, size_t inlen, u8 *out, size_t outlen, int oper) +{ + int r;/* + u8 buff[24] = {0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, + 0x4E, 0x4F, 0xEB, 0x69, 0x5B, 0xFF, 0x01, 0x20, 0xE1, 0xA9, 0x2D, 0xAE, 0x59, 0xD4, 0xD1, 0xCA}; + u8 outbuff[24] = {0};*/ + struct sc_rutoken_decipherinfo inf = { in, inlen, out, outlen }; + sc_security_env_t env; + env.key_ref[0] = keyid; + env.key_ref_len = 1; + env.algorithm = SC_ALGORITHM_GOST; + env.algorithm_flags = SC_RUTOKEN_OPTIONS_GOST_CRYPT_GAMM; + env.operation = SC_SEC_OPERATION_DECIPHER; + + /* set security env */ + trace("try to set SE key = %02X\n", keyid); + r = card->ops->set_security_env(card, &env, 0); + if (r) { + fprintf(stderr, "decipher failed: %d : %s\n", + r, sc_strerror(r)); + return 1; + } + trace("set SE - ok\n"); + /* cipher */ + r = card->ops->card_ctl(card, oper, &inf); + if (r < 0) { + fprintf(stderr, "decipher failed: %s\n", + sc_strerror(r)); + return 1; + } + trace("return %d\n", r); + return r; +} + +/* Decrypt a file */ + +int crypt_file(sc_card_t *card, u8 keyid, const char *szInFile, const char *szOutFile, int oper, u8* IV) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + int size = -1; + u8 *pBuf = NULL, *pOut = NULL; + + size = get_file(card, szInFile, &pBuf, oper == OP_ENCIPHER, IV); + trace("size of %s is %d\n", szInFile, size); + if(size > 0) + { + pOut = malloc(size); + size = rutoken_decipher + (card, keyid, pBuf, size, pOut, size, + oper == OP_ENCIPHER ? SC_CARDCTL_RUTOKEN_GOST_ENCIPHER : SC_CARDCTL_RUTOKEN_GOST_DECIPHER); + if ((size > 0) && (write_file(szOutFile, pOut, size) == size)) ret = SC_SUCCESS; + free(pBuf); + free(pOut); + } + return ret; +} + +/* external definitions */ +struct sc_profile_t; +extern int rutoken_erase(struct sc_profile_t *, sc_card_t *); +extern int rutoken_finalize_card(sc_card_t *); +extern int rutoken_init(struct sc_profile_t *, sc_card_t *); +/* Format and initialize file system */ +int format_card(sc_card_t *card) +{ + int ret = SC_ERROR_CARD_CMD_FAILED; + trace("enter\n"); + if (( ret = (rutoken_erase(NULL, card)) == SC_SUCCESS) && + ( ret = (rutoken_init(NULL, card)) == SC_SUCCESS) + ) + ret = rutoken_finalize_card(card); + return ret; +} + +int main(int argc, char *const argv[]) +{ + int err = 0, r, c, long_optind = 0; + const char *opt_driver = NULL; + sc_context_param_t ctx_param; + int opt_reader = -1, opt_debug = 0, opt_wait = 0, opt_key = 0, opt_is_IV, + opt_is_pin = 0, opt_is_sopin = 0, opt_is_input = 0, opt_is_output = 0; + char opt_pin[100] = {0}, opt_input[PATH_MAX] = {0}, opt_output[PATH_MAX] = {0}, opt_IV[16] = {0}; + + int operation = 0; + + sc_context_t *ctx = NULL; + sc_card_t *card = NULL; + + while (1) + { + c = getopt_long(argc, argv, "r:vc:wgeusk:i:o:p:I:F", options, + &long_optind); + if (c == -1) + break; + switch (c) { + case 'h': + case '?': + print_usage_and_die(app_name, options, option_help); + case 'r': + opt_reader = atoi(optarg); + break; + case 'v': + opt_debug++; + break; + case 'c': + opt_driver = optarg; + break; + case 'w': + opt_wait = 1; + break; + case 'g': + operation = OP_GET_INFO; + break; + case 'k': + opt_key = atoi(optarg); + opt_key = (opt_key / 10) * 0x10 + opt_key % 10; + break; + case 'I': + opt_is_IV = 1; + strncpy(opt_IV, optarg, 8); + break; + case 'u': + operation = OP_DECIPHER; + break; + case 'e': + operation = OP_ENCIPHER; + break; + case 's': + operation = OP_SIGN; + break; + case OPT_PIN: + if(opt_is_sopin || opt_is_pin) + { + fprintf(stderr, "You must specify only one pin\n"); + goto end; + } + opt_is_pin = 1; + strcpy(opt_pin, optarg); + break; + case OPT_SOPIN: + if(opt_is_sopin || opt_is_pin) + { + fprintf(stderr, "You must specify only one pin\n"); + goto end; + } + opt_is_sopin = 1; + strcpy(opt_pin, optarg); + break; + case 'i': + opt_is_input = 1; + strcpy(opt_input, optarg); + break; + case 'o': + opt_is_output = 1; + strcpy(opt_output, optarg); + break; + case 'F': + operation = OP_FORMAT; + break; + } + } + + /* create sc_context_t object */ + trace("\n"); + memset(&ctx_param, 0, sizeof(ctx_param)); + ctx_param.ver = 0; + ctx_param.app_name = app_name; + r = sc_context_create(&ctx, &ctx_param); + if (r) { + fprintf(stderr, "Failed to establish context: %s\n", + sc_strerror(r)); + return 1; + } + if (opt_debug) + ctx->debug = opt_debug; + if (opt_driver != NULL) { + err = sc_set_card_driver(ctx, opt_driver); + if (err) { + fprintf(stderr, "Driver '%s' not found!\n", + opt_driver); + err = -1; + goto end; + } + } + + trace("\n"); + err = connect_card(ctx, &card, opt_reader, 0, opt_wait, opt_debug); + if (err) + goto end; + + if(opt_is_pin || opt_is_sopin){ + /* verify */ + int tries_left = 0; + err = sc_verify(card, SC_AC_CHV, opt_is_sopin ? 1 : 2 , (u8*)opt_pin, strlen(opt_pin), &tries_left); + if(err) + { + fprintf(stderr, "verify failed %d\n", err); + goto end; + } + fprintf(stderr, "Verify ok\n"); + } + switch(operation) + { + case OP_GET_INFO: + if ((err = rutoken_info(card))) { + goto end; + } + break; + case OP_DECIPHER: + case OP_ENCIPHER: + if(!opt_key) + { + fprintf(stderr, "Not key\n"); + err = -1; + break; + } + if (!opt_is_input) + { + fprintf(stderr, "Not input file\n"); + err = -1; + break; + } + if (!opt_is_output) + { + fprintf(stderr, "Not output file\n"); + err = -1; + break; + } + err = crypt_file(card, opt_key, opt_input, opt_output, operation, opt_is_IV ? (u8*)opt_IV : NULL); + break; + case OP_FORMAT: + trace("OP_FORMAT\n"); + err = format_card(card); + if(err != SC_SUCCESS) fprintf(stderr, "Initialization failed\n"); + + break; + default: + printf("No operation --help\n"); + break; + } +end: + if (card) { + sc_unlock(card); + sc_disconnect_card(card, 0); + } + if (ctx) + sc_release_context(ctx); + return err; +}