opensc/src/libopensc/card-rutoken.c

1600 lines
44 KiB
C

/*
* 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 "config.h"
#if defined(HAVE_INTTYPES_H)
#include <inttypes.h>
#elif defined(HAVE_STDINT_H)
#include <stdint.h>
#elif defined(_MSC_VER)
typedef unsigned __int32 uint32_t;
typedef __int8 int8_t;
#else
#warning no uint32_t type available, please contact opensc-devel@opensc-project.org
#endif
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "internal.h"
#include "opensc.h"
#include "pkcs15.h"
#include "asn1.h"
#include "cardctl.h"
#ifdef ENABLE_OPENSSL
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/dsa.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#endif
struct auth_senv {
unsigned int algorithm;
};
typedef struct auth_senv auth_senv_t;
struct helper_acl_to_sec_attr
{
unsigned int ac_op;
size_t sec_attr_pos;
};
typedef struct helper_acl_to_sec_attr helper_acl_to_sec_attr_t;
static const helper_acl_to_sec_attr_t arr_convert_attr_df [] = {
{ SC_AC_OP_CREATE, 0 },
{ SC_AC_OP_CREATE, 1 },
{ SC_AC_OP_DELETE, 6 }
};
static const helper_acl_to_sec_attr_t arr_convert_attr_ef [] = {
{ SC_AC_OP_READ, 0 },
{ SC_AC_OP_UPDATE, 1 },
{ SC_AC_OP_WRITE, 1 },
{ SC_AC_OP_DELETE, 6 }
};
static const sc_SecAttrV2_t default_sec_attr = {
0x42,
0, 1, 0, 0, 0, 0, 1,
0, 0, 0, 0,
2, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
2, 0, 0, 0,
0, 0, 0, 0 /* reserve */
};
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 }, /* Aktiv Rutoken S */
{ "3b:6f:00:ff:00:56:75:61:54:6f:6b:6e:73:30:20:00:00:90:00", NULL, NULL, SC_CARD_TYPE_GENERIC_BASE, 0, NULL }, /* Aktiv uaToken S */
{ NULL, NULL, NULL, 0, 0, NULL }
};
static int rutoken_finish(sc_card_t *card)
{
SC_FUNC_CALLED(card->ctx, 1);
assert(card->drv_data);
free(card->drv_data);
SC_FUNC_RETURN(card->ctx, 1, SC_SUCCESS);
}
static int rutoken_match_card(sc_card_t *card)
{
SC_FUNC_CALLED(card->ctx, 1);
if (_sc_match_atr(card, rutoken_atrs, &card->type) >= 0)
{
sc_debug(card->ctx, "ATR recognized as Rutoken\n");
SC_FUNC_RETURN(card->ctx, 1, 1);
}
SC_FUNC_RETURN(card->ctx, 1, 0);
}
static int token_init(sc_card_t *card, const char *card_name)
{
unsigned int flags;
SC_FUNC_CALLED(card->ctx, 3);
card->name = card_name;
card->caps |= SC_CARD_CAP_RSA_2048 | SC_CARD_CAP_NO_FCI | SC_CARD_CAP_RNG;
card->drv_data = calloc(1, sizeof(auth_senv_t));
if (card->drv_data == NULL)
SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_OUT_OF_MEMORY);
flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1;
_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_FUNC_RETURN(card->ctx, 3, SC_SUCCESS);
}
static int rutoken_init(sc_card_t *card)
{
int ret;
SC_FUNC_CALLED(card->ctx, 1);
/* &rutoken_atrs[1] : { uaToken S ATR, NULL ATR } */
if (_sc_match_atr(card, &rutoken_atrs[1], &card->type) >= 0)
ret = token_init(card, "uaToken S card");
else
ret = token_init(card, "Rutoken S card");
SC_FUNC_RETURN(card->ctx, 1, 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. One tries left"},
{ 0x63C2, SC_ERROR_PIN_CODE_INCORRECT, "Authentication failed. Two tries left"},
{ 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, "DO 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 space in the token"},
{ 0x6A86, SC_ERROR_INCORRECT_PARAMETERS,"P1 or P2 invalid"},
{ 0x6A89, SC_ERROR_FILE_ALREADY_EXISTS, "File (DO) already exists"},
{ 0x6B00, SC_ERROR_INCORRECT_PARAMETERS,"Out of maximum 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, "Invalid access right. Already login"},
{ 0x9000, SC_NO_ERROR, NULL}
};
static int rutoken_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2)
{
size_t i;
for (i = 0; i < sizeof(rutoken_errors)/sizeof(rutoken_errors[0]); ++i) {
if (rutoken_errors[i].SWs == ((sw1 << 8) | sw2)) {
if ( rutoken_errors[i].errorstr )
sc_debug(card->ctx, "%s\n", rutoken_errors[i].errorstr);
sc_debug(card->ctx, "sw1 = %x, sw2 = %x", sw1, sw2);
return rutoken_errors[i].errorno;
}
}
sc_debug(card->ctx, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2);
return SC_ERROR_CARD_CMD_FAILED;
}
static void swap_pair(u8 *buf, size_t len)
{
size_t i;
u8 tmp;
for (i = 0; i + 1 < len; i += 2)
{
tmp = buf[i];
buf[i] = buf[i + 1];
buf[i + 1] = tmp;
}
}
static void swap_four(u8 *buf, size_t len)
{
size_t i;
u8 tmp;
for (i = 0; i + 3 < len; i += 4)
{
tmp = buf[i];
buf[i] = buf[i + 3];
buf[i + 3] = tmp;
swap_pair(&buf[i + 1], 2);
}
}
static int rutoken_list_files(sc_card_t *card, u8 *buf, size_t buflen)
{
sc_apdu_t apdu;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], previd[2];
const u8 *tag;
size_t taglen, len = 0;
int ret;
assert(card && card->ctx);
SC_FUNC_CALLED(card->ctx, 1);
assert(buf);
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0, 0);
for (;;)
{
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
apdu.le = sizeof(rbuf) - 2;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
if (apdu.sw1 == 0x6A && apdu.sw2 == 0x82)
break; /* Next file not found */
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, ret, "");
if (apdu.resplen <= 2)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_WRONG_LENGTH);
/* save first file(dir) ID */
tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2,
0x83, &taglen);
if (!tag || taglen != sizeof(previd))
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_UNKNOWN_DATA_RECEIVED);
memcpy(previd, tag, sizeof(previd));
if (len + sizeof(previd) <= buflen)
{
buf[len++] = previd[1];
buf[len++] = previd[0];
}
tag = sc_asn1_find_tag(card->ctx, apdu.resp + 2, apdu.resplen - 2,
0x82, &taglen);
if (!tag || taglen != 2)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_UNKNOWN_DATA_RECEIVED);
if (tag[0] == 0x38)
{
/* Select parent DF of the current DF */
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x03, 0);
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
apdu.le = sizeof(rbuf) - 2;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, ret, "");
}
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0x02);
apdu.lc = sizeof(previd);
apdu.data = previd;
apdu.datalen = sizeof(previd);
}
SC_FUNC_RETURN(card->ctx, 1, len);
}
static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file)
{
if (file->sec_attr && file->sec_attr_len == sizeof(sc_SecAttrV2_t))
{
sc_file_add_acl_entry(file, SC_AC_OP_SELECT,
SC_AC_NONE, SC_AC_KEY_REF_NONE);
if (file->sec_attr[0] & 0x40) /* if AccessMode.6 */
{
sc_debug(card->ctx, "SC_AC_OP_DELETE %i %i",
(int)(*(int8_t*)&file->sec_attr[1 +6]),
file->sec_attr[1+7 +6*4]);
sc_file_add_acl_entry(file, SC_AC_OP_DELETE,
(int)(*(int8_t*)&file->sec_attr[1 +6]),
file->sec_attr[1+7 +6*4]);
}
if (file->sec_attr[0] & 0x01) /* if AccessMode.0 */
{
sc_debug(card->ctx, (file->type == SC_FILE_TYPE_DF) ?
"SC_AC_OP_CREATE %i %i" : "SC_AC_OP_READ %i %i",
(int)(*(int8_t*)&file->sec_attr[1 +0]),
file->sec_attr[1+7 +0*4]);
sc_file_add_acl_entry(file,
(file->type == SC_FILE_TYPE_DF) ?
SC_AC_OP_CREATE : SC_AC_OP_READ,
(int)(*(int8_t*)&file->sec_attr[1 +0]),
file->sec_attr[1+7 +0*4]);
}
if (file->type == SC_FILE_TYPE_DF)
{
sc_file_add_acl_entry(file, SC_AC_OP_LIST_FILES,
SC_AC_NONE, SC_AC_KEY_REF_NONE);
}
else
if (file->sec_attr[0] & 0x02) /* if AccessMode.1 */
{
sc_debug(card->ctx, "SC_AC_OP_UPDATE %i %i",
(int)(*(int8_t*)&file->sec_attr[1 +1]),
file->sec_attr[1+7 +1*4]);
sc_file_add_acl_entry(file, SC_AC_OP_UPDATE,
(int)(*(int8_t*)&file->sec_attr[1 +1]),
file->sec_attr[1+7 +1*4]);
sc_debug(card->ctx, "SC_AC_OP_WRITE %i %i",
(int)(*(int8_t*)&file->sec_attr[1 +1]),
file->sec_attr[1+7 +1*4]);
sc_file_add_acl_entry(file, SC_AC_OP_WRITE,
(int)(*(int8_t*)&file->sec_attr[1 +1]),
file->sec_attr[1+7 +1*4]);
}
}
}
static int rutoken_select_file(sc_card_t *card,
const sc_path_t *in_path, sc_file_t **file_out)
{
sc_apdu_t apdu;
u8 buf[SC_MAX_APDU_BUFFER_SIZE], pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf;
sc_file_t *file = NULL;
size_t pathlen;
u8 t0, t1;
int ret;
assert(card && card->ctx);
SC_FUNC_CALLED(card->ctx, 1);
assert(in_path && sizeof(pathbuf) >= in_path->len);
memcpy(path, in_path->value, in_path->len);
pathlen = in_path->len;
/* p2 = 0; first record, return FCP */
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)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
break;
case SC_PATH_TYPE_PATH:
if (pathlen >= 2 && memcmp(path, "\x3F\x00", 2) == 0)
{
if (pathlen == 2)
break; /* only 3F00 supplied */
path += 2;
pathlen -= 2;
}
apdu.p1 = 0x08;
break;
case SC_PATH_TYPE_DF_NAME:
case SC_PATH_TYPE_FROM_CURRENT:
case SC_PATH_TYPE_PARENT:
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NOT_SUPPORTED);
default:
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
}
swap_pair(path, pathlen);
apdu.lc = pathlen;
apdu.data = path;
apdu.datalen = pathlen;
apdu.resp = buf;
apdu.resplen = sizeof(buf);
apdu.le = sizeof(buf) - 2;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
if (file_out == NULL)
{
if (apdu.sw1 == 0x61)
SC_FUNC_RETURN(card->ctx, 2, 0);
SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2));
}
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, ret, "");
if (apdu.resplen > 0 && apdu.resp[0] != 0x62) /* Tag 0x62 - FCP */
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_UNKNOWN_DATA_RECEIVED);
file = sc_file_new();
if (file == NULL)
SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY);
file->path = *in_path;
if (card->ops->process_fci == NULL)
{
sc_file_free(file);
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NOT_SUPPORTED);
}
if (apdu.resplen > 1 && apdu.resplen >= (size_t)apdu.resp[1] + 2)
{
ret = card->ops->process_fci(card, file, apdu.resp+2, apdu.resp[1]);
if (ret == SC_SUCCESS)
{
t0 = file->id & 0xFF;
t1 = (file->id >> 8) & 0xFF;
file->id = (t0 << 8) | t1;
t0 = file->size & 0xFF;
t1 = (file->size >> 8) & 0xFF;
file->size = (t0 << 8) | t1;
}
}
if (file->sec_attr && file->sec_attr_len == sizeof(sc_SecAttrV2_t))
set_acl_from_sec_attr(card, file);
else
ret = SC_ERROR_UNKNOWN_DATA_RECEIVED;
if (ret != SC_SUCCESS)
sc_file_free(file);
else
{
assert(file_out);
*file_out = file;
}
SC_FUNC_RETURN(card->ctx, 1, ret);
}
static int rutoken_construct_fci(sc_card_t *card, const sc_file_t *file,
u8 *out, size_t *outlen)
{
u8 buf[64], *p = out;
assert(card && card->ctx);
SC_FUNC_CALLED(card->ctx, 3);
assert(file && out && outlen);
assert(*outlen >= (size_t)(p - out) + 2);
*p++ = 0x62; /* FCP template */
p++; /* for length */
/* 0x80 - Number of data bytes in the file, excluding structural information */
buf[1] = (file->size >> 8) & 0xFF;
buf[0] = file->size & 0xFF;
sc_asn1_put_tag(0x80, buf, 2, p, *outlen - (p - out), &p);
/* 0x82 - File descriptor byte */
if (file->type_attr_len)
{
assert(sizeof(buf) >= file->type_attr_len);
memcpy(buf, file->type_attr, file->type_attr_len);
sc_asn1_put_tag(0x82, buf, file->type_attr_len,
p, *outlen - (p - out), &p);
}
else
{
switch (file->type)
{
case SC_FILE_TYPE_WORKING_EF:
buf[0] = 0x01;
break;
case SC_FILE_TYPE_DF:
buf[0] = 0x38;
break;
case SC_FILE_TYPE_INTERNAL_EF:
default:
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NOT_SUPPORTED);
}
buf[1] = 0;
sc_asn1_put_tag(0x82, buf, 2, p, *outlen - (p - out), &p);
}
/* 0x83 - File identifier */
buf[1] = (file->id >> 8) & 0xFF;
buf[0] = file->id & 0xFF;
sc_asn1_put_tag(0x83, buf, 2, p, *outlen - (p - out), &p);
if (file->prop_attr_len)
{
assert(sizeof(buf) >= file->prop_attr_len);
memcpy(buf, file->prop_attr, file->prop_attr_len);
sc_asn1_put_tag(0x85, buf, file->prop_attr_len,
p, *outlen - (p - out), &p);
}
if (file->sec_attr_len)
{
assert(sizeof(buf) >= file->sec_attr_len);
memcpy(buf, file->sec_attr, file->sec_attr_len);
sc_asn1_put_tag(0x86, buf, file->sec_attr_len,
p, *outlen - (p - out), &p);
}
out[1] = p - out - 2; /* length */
*outlen = p - out;
SC_FUNC_RETURN(card->ctx, 3, 0);
}
static int set_sec_attr_from_acl(sc_card_t *card, sc_file_t *file)
{
const helper_acl_to_sec_attr_t *conv_attr;
size_t i, n_conv_attr;
const sc_acl_entry_t *entry;
sc_SecAttrV2_t attr = { 0 };
int ret = SC_NO_ERROR;
SC_FUNC_CALLED(card->ctx, 3);
if (file->type == SC_FILE_TYPE_DF)
{
conv_attr = arr_convert_attr_df;
n_conv_attr = sizeof(arr_convert_attr_df)/sizeof(arr_convert_attr_df[0]);
}
else
{
conv_attr = arr_convert_attr_ef;
n_conv_attr = sizeof(arr_convert_attr_ef)/sizeof(arr_convert_attr_ef[0]);
}
sc_debug(card->ctx, "file->type = %i", file->type);
for (i = 0; i < n_conv_attr; ++i)
{
entry = sc_file_get_acl_entry(file, conv_attr[i].ac_op);
if (entry && (entry->method == SC_AC_CHV || entry->method == SC_AC_NONE
|| entry->method == SC_AC_NEVER)
)
{
/* AccessMode.[conv_attr[i].sec_attr_pos] */
attr[0] |= 1 << conv_attr[i].sec_attr_pos;
sc_debug(card->ctx, "AccessMode.%u, attr[0]=0x%x",
conv_attr[i].sec_attr_pos, attr[0]);
attr[1 + conv_attr[i].sec_attr_pos] = (u8)entry->method;
sc_debug(card->ctx, "method %u", (u8)entry->method);
if (entry->method == SC_AC_CHV)
{
attr[1+7 + conv_attr[i].sec_attr_pos*4] = (u8)entry->key_ref;
sc_debug(card->ctx, "key_ref %u", (u8)entry->key_ref);
}
}
else
{
sc_debug(card->ctx, "ACL (%u) not set, set default sec_attr",
conv_attr[i].ac_op);
memcpy(attr, default_sec_attr, sizeof(attr));
break;
}
}
ret = sc_file_set_sec_attr(file, attr, sizeof(attr));
SC_FUNC_RETURN(card->ctx, 3, ret);
}
static int rutoken_create_file(sc_card_t *card, sc_file_t *file)
{
int ret;
assert(card && card->ctx);
SC_FUNC_CALLED(card->ctx, 1);
assert(file);
if (file->sec_attr_len == 0)
{
ret = set_sec_attr_from_acl(card, file);
SC_TEST_RET(card->ctx, ret, "Set sec_attr from ACL failed");
}
assert(iso_ops && iso_ops->create_file);
ret = iso_ops->create_file(card, file);
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_debug(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 == sizeof(sbuf))
{
sbuf[1] = path->value[0];
sbuf[0] = path->value[1];
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x00, 0x00);
apdu.lc = sizeof(sbuf);
apdu.datalen = sizeof(sbuf);
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, sc_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)
{
sc_apdu_t apdu;
int ret;
SC_FUNC_CALLED(card->ctx, 1);
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, ref_qualifier);
ret = sc_transmit_apdu(card, &apdu);
if (ret == SC_SUCCESS && ((apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
|| apdu.sw1 == 0x63)
)
{
/* sw1 == 0x63 - may be already login with other ref_qualifier
* sw1 == 0x90 && sw2 == 0x00 - already login with ref_qualifier
*/
/* RESET ACCESS RIGHTS */
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0x00, 0x00);
apdu.cla = 0x80;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, ret, "Reset access rights failed");
}
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;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (ret == SC_ERROR_PIN_CODE_INCORRECT && tries_left)
{
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, ref_qualifier);
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (ret == SC_ERROR_PIN_CODE_INCORRECT)
*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;
sc_path_t path;
int ret;
SC_FUNC_CALLED(card->ctx, 1);
sc_format_path("3F00", &path);
ret = rutoken_select_file(card, &path, NULL);
SC_TEST_RET(card->ctx, ret, "Select MF failed");
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0x00, 0x00);
apdu.cla = 0x80;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_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)
{
sc_apdu_t apdu;
int ret;
SC_FUNC_CALLED(card->ctx, 1);
if (old && oldlen)
{
ret = rutoken_verify(card, type, ref_qualifier, old, oldlen, tries_left);
SC_TEST_RET(card->ctx, ret, "Invalid 'old' pass");
}
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier);
apdu.lc = newlen;
apdu.datalen = newlen;
apdu.data = newref;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
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)
{
#ifdef FORCE_VERIFY_RUTOKEN
int left;
#endif
sc_apdu_t apdu;
int ret;
SC_FUNC_CALLED(card->ctx, 1);
#ifdef FORCE_VERIFY_RUTOKEN
if (puk && puklen)
{
ret = rutoken_verify(card, type, ref_qualifier, puk, puklen, &left);
sc_debug(card->ctx, "Tries left: %i\n", left);
SC_TEST_RET(card->ctx, ret, "Invalid 'puk' pass");
}
#endif
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2c, 0x03, ref_qualifier);
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_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)
{
sc_apdu_t apdu;
int ret;
SC_FUNC_CALLED(card->ctx, 1);
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 3, se_num);
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_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_apdu_t apdu;
auth_senv_t *senv;
u8 data[3] = { 0x83, 0x01 };
int ret;
SC_FUNC_CALLED(card->ctx, 1);
if (!env)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
senv = (auth_senv_t*)card->drv_data;
if (!senv)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INTERNAL);
if (env->algorithm == SC_ALGORITHM_RSA)
{
senv->algorithm = SC_ALGORITHM_RSA_RAW;
SC_FUNC_RETURN(card->ctx, 1, SC_SUCCESS);
}
senv->algorithm = SC_ALGORITHM_GOST;
if (env->key_ref_len != 1)
{
sc_debug(card->ctx, "No or invalid key reference\n");
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
}
data[2] = env->key_ref[0];
/* select component */
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 1, 0);
apdu.lc = apdu.datalen = sizeof(data);
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:
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
}
/* set SE */
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_FUNC_RETURN(card->ctx, 1, ret);
}
static void rutoken_set_do_hdr(u8 *data, size_t *data_len, sc_DOHdrV2_t *hdr)
{
u8 buf[64], *p = data;
assert(hdr && data && data_len);
/* 0x80 - Number of data bytes in the file, excluding structural information */
buf[1] = (hdr->wDOBodyLen >> 8) & 0xFF;
buf[0] = hdr->wDOBodyLen & 0xFF;
sc_asn1_put_tag(0x80, buf, 2, p, *data_len - (p - data), &p);
/* 0x83 - Type and ID */
buf[0] = hdr->OTID.byObjectType;
buf[1] = hdr->OTID.byObjectID;
sc_asn1_put_tag(0x83, buf, 2, p, *data_len - (p - data), &p);
/* 0x85 - Options, Flags and Max count of try */
buf[0] = hdr->OP.byObjectOptions;
buf[1] = hdr->OP.byObjectFlags;
buf[2] = hdr->OP.byObjectTry;
sc_asn1_put_tag(0x85, buf, 3, p, *data_len - (p - data), &p);
assert(sizeof(buf) >= sizeof(hdr->SA_V2));
memcpy(buf, hdr->SA_V2, sizeof(hdr->SA_V2));
sc_asn1_put_tag(0x86, buf, sizeof(hdr->SA_V2), p, *data_len - (p - data), &p);
assert(*data_len >= (size_t)(p - data));
*data_len = p - data;
}
static int rutoken_key_gen(sc_card_t *card, sc_DOHdrV2_t *pHdr)
{
u8 data[SC_MAX_APDU_BUFFER_SIZE];
size_t data_len = sizeof(data);
sc_apdu_t apdu;
int ret;
SC_FUNC_CALLED(card->ctx, 3);
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, &data_len, pHdr);
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x65);
apdu.data = data;
apdu.datalen = apdu.lc = data_len;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
}
SC_FUNC_RETURN(card->ctx, 3, ret);
}
static int rutoken_create_do(sc_card_t *card, sc_DO_V2_t * pDO)
{
u8 data[SC_MAX_APDU_BUFFER_SIZE];
size_t data_len = sizeof(data);
sc_apdu_t apdu;
int ret;
SC_FUNC_CALLED(card->ctx, 3);
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, &data_len, &pDO->HDR);
assert(sizeof(data) >= data_len + pDO->HDR.wDOBodyLen + 2);
ret = sc_asn1_put_tag(0xA5, pDO->abyDOBody, pDO->HDR.wDOBodyLen,
data + data_len, sizeof(data) - data_len, NULL);
if (ret == SC_SUCCESS)
data_len += pDO->HDR.wDOBodyLen + 2;
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xda, 0x01, 0x62);
apdu.data = data;
apdu.datalen = apdu.lc = data_len;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
}
SC_FUNC_RETURN(card->ctx, 3, ret);
}
static int rutoken_get_do_info(sc_card_t *card, sc_DO_INFO_t * pInfo)
{
u8 data[1];
sc_apdu_t apdu;
int ret;
SC_FUNC_CALLED(card->ctx, 3);
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_next:
apdu.p2 = 0x02;
case select_by_id:
data[0] = pInfo->DoId;
apdu.data = data;
apdu.datalen = sizeof(data);
apdu.lc = sizeof(data);
break;
default:
SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_INVALID_ARGUMENTS);
break;
}
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
}
SC_FUNC_RETURN(card->ctx, 3, ret);
}
static int rutoken_delete_do(sc_card_t *card, u8 *pId)
{
u8 data[1];
sc_apdu_t apdu;
int ret;
SC_FUNC_CALLED(card->ctx, 3);
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);
data[0] = *pId;
apdu.data = data;
apdu.datalen = sizeof(data);
apdu.lc = sizeof(data);
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
}
SC_FUNC_RETURN(card->ctx, 3, 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)
{
u8 buf[248]; /* 248 (cipher_chunk) <= SC_MAX_APDU_BUFFER_SIZE */
size_t len, outlen_tail = outlen;
int ret;
sc_apdu_t apdu;
SC_FUNC_CALLED(card->ctx, 3);
sc_debug(card->ctx, ": crgram_len %i; outlen %i", crgram_len, outlen);
if (!out)
SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_INVALID_ARGUMENTS);
if (crgram_len < 16 || ((crgram_len) % 8))
SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_WRONG_LENGTH);
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, p1, p2);
do
{
len = (crgram_len > sizeof(buf)) ? sizeof(buf) : 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;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
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);
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, 3, ret);
}
/* Launcher for cipher */
static int rutoken_cipher_gost(sc_card_t *card,
struct sc_rutoken_decipherinfo *ptr, char is_encipher)
{
int ret;
if (is_encipher)
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, 3);
if (!in || !out || olen != 4 || ilen == 0)
SC_FUNC_RETURN(card->ctx, 3, 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;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
} while (ret == SC_NO_ERROR && ilen != 0);
SC_FUNC_RETURN(card->ctx, 3, ret);
}
/* RSA emulation */
#ifdef ENABLE_OPENSSL
static int rutoken_get_prkey_from_bin(const u8 *data, size_t datalen,
struct sc_pkcs15_prkey **key)
{
uint32_t bitlen;
size_t i, len;
struct sc_pkcs15_prkey_rsa *key_rsa;
if (!data || !key || *key != NULL)
return -1;
if (datalen < 14 + sizeof(uint32_t))
return -1;
/* Check header */
if ( data[0] != 2 || data[1] != 1
|| data[2] != 0x07 /* Type */
|| data[3] != 0x02 /* Version */
/* aiKeyAlg */
|| data[6] != 0 || data[7] != 0xA4 || data[8] != 0 || data[9] != 0
/* magic "RSA2" */
|| data[10] != 0x52 || data[11] != 0x53
|| data[12] != 0x41 || data[13] != 0x32
)
return -1;
len = 14;
/* bitlen */
bitlen = 0;
for (i = 0; i < sizeof(uint32_t); ++i)
bitlen += (uint32_t)data[len++] << i*8;
if (bitlen % 16)
return -1;
if (datalen - len < sizeof(uint32_t) + bitlen/8 * 2 + bitlen/16 * 5)
return -1;
*key = calloc(1, sizeof(struct sc_pkcs15_prkey));
if (!*key)
return -1;
key_rsa = &(*key)->u.rsa;
key_rsa->exponent.data = malloc(sizeof(uint32_t));
key_rsa->modulus.data = malloc(bitlen/8);
key_rsa->p.data = malloc(bitlen/16);
key_rsa->q.data = malloc(bitlen/16);
key_rsa->dmp1.data = malloc(bitlen/16);
key_rsa->dmq1.data = malloc(bitlen/16);
key_rsa->iqmp.data = malloc(bitlen/16);
key_rsa->d.data = malloc(bitlen/8);
if (!key_rsa->exponent.data || !key_rsa->modulus.data
|| !key_rsa->p.data || !key_rsa->q.data
|| !key_rsa->dmp1.data || !key_rsa->dmq1.data
|| !key_rsa->iqmp.data || !key_rsa->d.data
)
{
free(key_rsa->exponent.data);
free(key_rsa->modulus.data);
free(key_rsa->p.data);
free(key_rsa->q.data);
free(key_rsa->dmp1.data);
free(key_rsa->dmq1.data);
free(key_rsa->iqmp.data);
free(key_rsa->d.data);
memset(key_rsa, 0, sizeof(*key_rsa));
free(*key);
*key = NULL;
return -1;
}
#define MEMCPY_KEYRSA_REVERSE_DATA(NAME, size) /* set key_rsa->NAME.len */ \
do { \
for (i = 0; i < (size); ++i) \
if (data[len + (size) - 1 - i] != 0) \
break; \
for (; i < (size); ++i) \
key_rsa->NAME.data[key_rsa->NAME.len++] = data[len + (size) - 1 - i]; \
len += (size); \
} while (0)
MEMCPY_KEYRSA_REVERSE_DATA(exponent, sizeof(uint32_t)); /* pubexp */
MEMCPY_KEYRSA_REVERSE_DATA(modulus, bitlen/8); /* modulus */
MEMCPY_KEYRSA_REVERSE_DATA(p, bitlen/16); /* prime1 */
MEMCPY_KEYRSA_REVERSE_DATA(q, bitlen/16); /* prime2 */
MEMCPY_KEYRSA_REVERSE_DATA(dmp1, bitlen/16); /* exponent1 */
MEMCPY_KEYRSA_REVERSE_DATA(dmq1, bitlen/16); /* exponent2 */
MEMCPY_KEYRSA_REVERSE_DATA(iqmp, bitlen/16); /* coefficient */
MEMCPY_KEYRSA_REVERSE_DATA(d, bitlen/8); /* privateExponent */
(*key)->algorithm = SC_ALGORITHM_RSA;
return 0;
}
static int rutoken_get_current_fileid(sc_card_t *card, u8 id[2])
{
sc_apdu_t apdu;
int ret;
SC_FUNC_CALLED(card->ctx, 3);
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x11);
apdu.resp = id;
apdu.resplen = 2;
apdu.le = 2;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
swap_pair(id, 2);
SC_FUNC_RETURN(card->ctx, 3, 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 = rutoken_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, 3);
r = rutoken_read_prkey(card, &key);
if (r < 0)
SC_FUNC_RETURN(card->ctx, 3, 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);
SC_FUNC_RETURN(card->ctx, 3, r);
}
static int cipher_ext(sc_card_t *card, const u8 *data, size_t len,
u8 *out, size_t out_len,
int sign /* sign==1 -> Sidn; sign==0 -> decipher */)
{
char error[1024];
EVP_PKEY *pkey = NULL;
int ret, r;
SC_FUNC_CALLED(card->ctx, 3);
if (out_len < len)
SC_FUNC_RETURN(card->ctx, 3, SC_ERROR_INVALID_ARGUMENTS);
ret = extract_key(card, &pkey);
if (ret == SC_SUCCESS)
{
if (sign)
r = RSA_PKCS1_SSLeay()->rsa_priv_enc(len, data, out,
pkey->pkey.rsa, RSA_PKCS1_PADDING);
else
{
r = RSA_PKCS1_SSLeay()->rsa_priv_dec(len, data, out,
pkey->pkey.rsa, RSA_PKCS1_PADDING);
ret = r;
}
if ( r < 0)
{
ret = SC_ERROR_INTERNAL;
ERR_load_crypto_strings();
ERR_error_string(ERR_get_error(), error);
sc_debug(card->ctx, error);
ERR_free_strings();
}
}
if (pkey)
EVP_PKEY_free(pkey);
SC_FUNC_RETURN(card->ctx, 3, ret);
}
static int rutoken_decipher(sc_card_t *card,
const u8 * data, size_t datalen,
u8 * out, size_t outlen)
{
int ret;
auth_senv_t *senv = (auth_senv_t *)card->drv_data;
SC_FUNC_CALLED(card->ctx, 1);
if (!senv)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INTERNAL);
if (senv->algorithm == SC_ALGORITHM_GOST)
{
ret = rutoken_cipher_p(card, data, datalen, out, outlen, 0x80, 0x86, 1);
}
else if (senv->algorithm == SC_ALGORITHM_RSA_RAW)
{
/* decipher */
ret = cipher_ext(card, data, datalen, out, outlen, 0);
}
else
ret = SC_ERROR_NOT_SUPPORTED;
SC_FUNC_RETURN(card->ctx, 1, ret);
}
static int rutoken_compute_signature(struct sc_card *card,
const u8 * data, size_t datalen,
u8 * out, size_t outlen)
{
int ret;
auth_senv_t *senv = (auth_senv_t *)card->drv_data;
SC_FUNC_CALLED(card->ctx, 1);
if (!senv)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INTERNAL);
if (senv->algorithm == SC_ALGORITHM_GOST)
{
ret = rutoken_compute_mac_gost(card, data, datalen, out, outlen);
}
else if (senv->algorithm == SC_ALGORITHM_RSA_RAW)
{
/* sign */
ret = cipher_ext(card, data, datalen, out, outlen, 1);
}
else
ret = SC_ERROR_NOT_SUPPORTED;
SC_FUNC_RETURN(card->ctx, 1, ret);
}
#endif /* ENABLE_OPENSSL */
static int rutoken_get_challenge(sc_card_t *card, u8 *rnd, size_t count)
{
sc_apdu_t apdu;
u8 rbuf[32];
size_t n;
int ret = SC_ERROR_INVALID_ARGUMENTS; /* if count == 0 */
SC_FUNC_CALLED(card->ctx, 1);
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");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, ret, "Get challenge failed");
if (apdu.resplen != sizeof(rbuf))
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_UNKNOWN);
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_serial(sc_card_t *card, sc_serial_number_t *serial)
{
sc_apdu_t apdu;
int ret;
SC_FUNC_CALLED(card->ctx, 3);
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0x81);
apdu.resp = serial->value;
apdu.resplen = sizeof(serial->value);
apdu.le = 4;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
serial->len = apdu.resplen;
swap_four(serial->value, serial->len);
SC_FUNC_RETURN(card->ctx, 3, ret);
}
static int rutoken_get_info(sc_card_t *card, void *buff)
{
sc_apdu_t apdu;
u8 rbuf[8];
int ret;
SC_FUNC_CALLED(card->ctx, 3);
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0x89);
apdu.resp = rbuf;
apdu.resplen = sizeof(rbuf);
apdu.le = sizeof(rbuf);
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
if (ret == SC_SUCCESS)
memcpy(buff, apdu.resp, apdu.resplen);
SC_FUNC_RETURN(card->ctx, 3, ret);
}
static int rutoken_format(sc_card_t *card, int apdu_ins)
{
int ret;
sc_apdu_t apdu;
SC_FUNC_CALLED(card->ctx, 3);
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, apdu_ins, 0x00, 0x00);
apdu.cla = 0x80;
ret = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, ret, "APDU transmit failed");
ret = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_FUNC_RETURN(card->ctx, 3, 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);
ret = SC_ERROR_NOT_SUPPORTED;
break;
}
}
SC_FUNC_RETURN(card->ctx, 1, ret);
}
static struct sc_card_driver* get_rutoken_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;
/* read_binary */
rutoken_ops.write_binary = NULL;
/* update_binary */
rutoken_ops.read_record = NULL;
rutoken_ops.write_record = NULL;
rutoken_ops.append_record = NULL;
rutoken_ops.update_record = NULL;
rutoken_ops.select_file = rutoken_select_file;
rutoken_ops.get_response = NULL;
rutoken_ops.get_challenge = rutoken_get_challenge;
rutoken_ops.verify = rutoken_verify;
rutoken_ops.logout = rutoken_logout;
rutoken_ops.restore_security_env = rutoken_restore_security_env;
rutoken_ops.set_security_env = rutoken_set_security_env;
#ifdef ENABLE_OPENSSL
rutoken_ops.decipher = rutoken_decipher;
rutoken_ops.compute_signature = rutoken_compute_signature;
#else
rutoken_ops.decipher = NULL;
rutoken_ops.compute_signature = NULL;
#endif
rutoken_ops.change_reference_data = rutoken_change_reference_data;
rutoken_ops.reset_retry_counter = rutoken_reset_retry_counter;
rutoken_ops.create_file = rutoken_create_file;
rutoken_ops.delete_file = rutoken_delete_file;
rutoken_ops.list_files = rutoken_list_files;
rutoken_ops.check_sw = rutoken_check_sw;
rutoken_ops.card_ctl = rutoken_card_ctl;
/* process_fci */
rutoken_ops.construct_fci = rutoken_construct_fci;
rutoken_ops.pin_cmd = NULL;
return &rutoken_drv;
}
struct sc_card_driver * sc_get_rutoken_driver(void)
{
return get_rutoken_driver();
}