Репозиторий Sisyphus
Последнее обновление: 1 октября 2023 | Пакетов: 18631 | Посещений: 37705042
en ru br
Репозитории 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
www.altlinux.org/Changes

Группа :: Система/Настройка/Оборудование
Пакет: 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(&param_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, &param_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(&paramkey, 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, &paramkey);
+	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;
+}
+
 
дизайн и разработка: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
текущий майнтейнер: Michael Shigorin