Репозитории ALT
S: | 0.23.0-alt2 |
5.1: | 0.11.9-alt3 |
4.1: | 0.11.6-alt0.M41.1 |
4.0: | 0.11.6-alt0.M40.1 |
3.0: | 0.8.1-alt4.1 |
Группа :: Система/Настройка/Оборудование
Пакет: opensc
Главная Изменения Спек Патчи Sources Загрузить Gear Bugs and FR Repocop
Патч: opensc-0.11.4-alt4-super.patch
Скачать
Скачать
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 <rutoken@rutoken.ru>
+ * Copyright (C) 2007 Eugene Hermann <rutoken@rutoken.ru>
+ *
+ * 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 <sys/types.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include "pkcs15.h"
+
+#define BIG_ENDIAN_RUTOKEN
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <opensc/asn1.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#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 <rutoken@rutoken.ru>
+ * Copyright (C) 2007 Eugene Hermann <rutoken@rutoken.ru>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <opensc/opensc.h>
+#include <opensc/pkcs15.h>
+
+/* 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 <rutoken@rutoken.ru>
+ * Copyright (C) 2007 Eugene Hermann <rutoken@rutoken.ru>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <opensc/opensc.h>
+#include <opensc/cardctl.h>
+#include <opensc/log.h>
+#include <opensc/pkcs15.h>
+
+#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 <rutoken@rutoken.ru>
+ * Copyright (C) 2007 Eugene Hermann <rutoken@rutoken.ru>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <opensc/opensc.h>
+#include <opensc/cardctl.h>
+#include <opensc/log.h>
+#include <opensc/pkcs15.h>
+#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 <rutoken@rutoken.ru>
+ * Copyright (C) 2007 Eugene Hermann <rutoken@rutoken.ru>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <opensc/opensc.h>
+#include <opensc/cardctl.h>
+#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 <arg> [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;
+}
+