Add new rutoken_ecp driver by Aktiv Co. / Aleksey Samsonov
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3696 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
parent
490d1b84aa
commit
7931ded481
|
@ -39,7 +39,8 @@ 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-entersafe.c \
|
||||
card-incrypto34.c card-piv.c card-muscle.c card-acos5.c \
|
||||
card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c\
|
||||
card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \
|
||||
card-rtecp.c \
|
||||
\
|
||||
pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \
|
||||
pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \
|
||||
|
|
|
@ -28,6 +28,7 @@ OBJECTS = \
|
|||
card-oberthur.obj card-belpic.obj card-atrust-acos.obj card-entersafe.obj \
|
||||
card-incrypto34.obj card-piv.obj card-muscle.obj card-acos5.obj \
|
||||
card-asepcos.obj card-akis.obj card-gemsafeV1.obj card-rutoken.obj \
|
||||
card-rtecp.obj \
|
||||
\
|
||||
pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \
|
||||
pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \
|
||||
|
|
|
@ -0,0 +1,850 @@
|
|||
/*
|
||||
* card-rtecp.c: Support for Rutoken ECP cards
|
||||
*
|
||||
* Copyright (C) 2009 Aleksey Samsonov <samsonov@guardant.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 <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "cardctl.h"
|
||||
#include "asn1.h"
|
||||
|
||||
static const struct sc_card_operations *iso_ops = NULL;
|
||||
static struct sc_card_operations rtecp_ops;
|
||||
|
||||
static struct sc_card_driver rtecp_drv = {
|
||||
"Rutoken ECP driver",
|
||||
"rutoken_ecp",
|
||||
&rtecp_ops,
|
||||
NULL, 0, NULL
|
||||
};
|
||||
|
||||
static struct sc_atr_table rtecp_atrs[] = {
|
||||
/* Rutoken ECP */
|
||||
{ "3B:8B:01:52:75:74:6F:6B:65:6E:20:45:43:50:A0",
|
||||
NULL, NULL, SC_CARD_TYPE_GENERIC_BASE, 0, NULL },
|
||||
{ NULL, NULL, NULL, 0, 0, NULL }
|
||||
};
|
||||
|
||||
static int rtecp_match_card(sc_card_t *card)
|
||||
{
|
||||
assert(card && card->ctx);
|
||||
if (_sc_match_atr(card, rtecp_atrs, &card->type) >= 0)
|
||||
SC_FUNC_RETURN(card->ctx, 1, 1);
|
||||
SC_FUNC_RETURN(card->ctx, 1, 0);
|
||||
}
|
||||
|
||||
static int rtecp_init(sc_card_t *card)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
assert(card && card->ctx);
|
||||
card->name = "Rutoken ECP card";
|
||||
card->caps |= SC_CARD_CAP_RSA_2048 | SC_CARD_CAP_NO_FCI | SC_CARD_CAP_RNG;
|
||||
card->cla = 0;
|
||||
|
||||
flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN
|
||||
| SC_ALGORITHM_RSA_PAD_NONE | SC_ALGORITHM_RSA_HASH_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, 1280, flags, 0);
|
||||
_sc_card_add_rsa_alg(card, 1536, flags, 0);
|
||||
_sc_card_add_rsa_alg(card, 1792, flags, 0);
|
||||
_sc_card_add_rsa_alg(card, 2048, flags, 0);
|
||||
|
||||
SC_FUNC_RETURN(card->ctx, 2, 0);
|
||||
}
|
||||
|
||||
static void reverse(unsigned char *buf, size_t len)
|
||||
{
|
||||
unsigned char tmp;
|
||||
size_t i;
|
||||
|
||||
assert(buf || len == 0);
|
||||
for (i = 0; i < len / 2; ++i)
|
||||
{
|
||||
tmp = buf[i];
|
||||
buf[i] = buf[len - 1 - i];
|
||||
buf[len - 1 - i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int sec_attr_to_method(unsigned int attr)
|
||||
{
|
||||
if (attr == 0xFF)
|
||||
return SC_AC_NEVER;
|
||||
else if (attr == 0)
|
||||
return SC_AC_NONE;
|
||||
else if (attr & 0x03)
|
||||
return SC_AC_CHV;
|
||||
else
|
||||
return SC_AC_UNKNOWN;
|
||||
}
|
||||
|
||||
static unsigned long sec_attr_to_key_ref(unsigned int attr)
|
||||
{
|
||||
if (attr == 1 || attr == 2)
|
||||
return attr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int to_sec_attr(unsigned int method, unsigned int key_ref)
|
||||
{
|
||||
if (method == SC_AC_NEVER || method == SC_AC_NONE)
|
||||
return method;
|
||||
if (method == SC_AC_CHV && (key_ref == 1 || key_ref == 2))
|
||||
return key_ref;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_acl_from_sec_attr(sc_card_t *card, sc_file_t *file)
|
||||
{
|
||||
unsigned int method;
|
||||
unsigned long key_ref;
|
||||
|
||||
assert(card && card->ctx && file);
|
||||
assert(file->sec_attr && file->sec_attr_len == SEC_ATTR_SIZE);
|
||||
assert(1 + 6 < SEC_ATTR_SIZE);
|
||||
|
||||
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 */
|
||||
{
|
||||
method = sec_attr_to_method(file->sec_attr[1 + 6]);
|
||||
key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 6]);
|
||||
if (card->ctx->debug >= 3)
|
||||
sc_debug(card->ctx, "SC_AC_OP_DELETE %i %lu\n",
|
||||
(int)method, key_ref);
|
||||
sc_file_add_acl_entry(file, SC_AC_OP_DELETE, method, key_ref);
|
||||
}
|
||||
if (file->sec_attr[0] & 0x01) /* if AccessMode.0 */
|
||||
{
|
||||
method = sec_attr_to_method(file->sec_attr[1 + 0]);
|
||||
key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 0]);
|
||||
if (card->ctx->debug >= 3)
|
||||
sc_debug(card->ctx, (file->type == SC_FILE_TYPE_DF) ?
|
||||
"SC_AC_OP_CREATE %i %lu\n"
|
||||
: "SC_AC_OP_READ %i %lu\n",
|
||||
(int)method, key_ref);
|
||||
sc_file_add_acl_entry(file, (file->type == SC_FILE_TYPE_DF) ?
|
||||
SC_AC_OP_CREATE : SC_AC_OP_READ, method, key_ref);
|
||||
}
|
||||
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 */
|
||||
{
|
||||
method = sec_attr_to_method(file->sec_attr[1 + 1]);
|
||||
key_ref = sec_attr_to_key_ref(file->sec_attr[1 + 1]);
|
||||
if (card->ctx->debug >= 3)
|
||||
sc_debug(card->ctx, "SC_AC_OP_UPDATE %i %lu\n",
|
||||
(int)method, key_ref);
|
||||
sc_file_add_acl_entry(file, SC_AC_OP_UPDATE, method, key_ref);
|
||||
if (card->ctx->debug >= 3)
|
||||
sc_debug(card->ctx, "SC_AC_OP_WRITE %i %lu\n",
|
||||
(int)method, key_ref);
|
||||
sc_file_add_acl_entry(file, SC_AC_OP_WRITE, method, key_ref);
|
||||
}
|
||||
}
|
||||
|
||||
static int set_sec_attr_from_acl(sc_card_t *card, sc_file_t *file)
|
||||
{
|
||||
const sc_acl_entry_t *entry;
|
||||
u8 sec_attr[SEC_ATTR_SIZE] = { 0 };
|
||||
int r;
|
||||
|
||||
assert(card && card->ctx && file);
|
||||
assert(!file->sec_attr && file->sec_attr_len == 0);
|
||||
assert(1 + 6 < sizeof(sec_attr));
|
||||
|
||||
entry = sc_file_get_acl_entry(file, SC_AC_OP_DELETE);
|
||||
if (entry)
|
||||
{
|
||||
sec_attr[0] |= 0x40;
|
||||
sec_attr[1 + 6] = to_sec_attr(entry->method, entry->key_ref);
|
||||
}
|
||||
if (file->type == SC_FILE_TYPE_DF)
|
||||
{
|
||||
entry = sc_file_get_acl_entry(file, SC_AC_OP_CREATE);
|
||||
if (entry)
|
||||
{
|
||||
/* ATTR: Create DF/EF file */
|
||||
sec_attr[0] |= 0x01;
|
||||
sec_attr[1 + 0] = to_sec_attr(entry->method, entry->key_ref);
|
||||
/* ATTR: Create Internal EF (RSF) file */
|
||||
sec_attr[0] |= 0x02;
|
||||
sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry = sc_file_get_acl_entry(file, SC_AC_OP_READ);
|
||||
if (entry)
|
||||
{
|
||||
sec_attr[0] |= 0x01;
|
||||
sec_attr[1 + 0] = to_sec_attr(entry->method, entry->key_ref);
|
||||
}
|
||||
entry = sc_file_get_acl_entry(file, SC_AC_OP_WRITE);
|
||||
if (entry)
|
||||
{
|
||||
sec_attr[0] |= 0x02;
|
||||
sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref);
|
||||
}
|
||||
entry = sc_file_get_acl_entry(file, SC_AC_OP_UPDATE);
|
||||
if (entry)
|
||||
{
|
||||
/* rewrite if sec_attr[1 + 1] already set */
|
||||
sec_attr[0] |= 0x02;
|
||||
sec_attr[1 + 1] = to_sec_attr(entry->method, entry->key_ref);
|
||||
}
|
||||
}
|
||||
/* FIXME: Find the best solution */
|
||||
if (file->path.len == 2 && !memcmp(file->path.value, "\x3F\x00", 2))
|
||||
{
|
||||
/* ATTR: Put data */
|
||||
sec_attr[0] |= 0x04;
|
||||
sec_attr[1 + 2] = 1; /* so-pin reference */
|
||||
}
|
||||
r = sc_file_set_sec_attr(file, sec_attr, sizeof(sec_attr));
|
||||
SC_FUNC_RETURN(card->ctx, 3, r);
|
||||
}
|
||||
|
||||
static int rtecp_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;
|
||||
int r;
|
||||
|
||||
assert(card && card->ctx && in_path);
|
||||
assert(sizeof(pathbuf) >= in_path->len);
|
||||
memcpy(path, in_path->value, in_path->len);
|
||||
pathlen = in_path->len;
|
||||
|
||||
/* p2 = 0; first record, return FCI */
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0);
|
||||
switch (in_path->type)
|
||||
{
|
||||
case SC_PATH_TYPE_FILE_ID:
|
||||
apdu.p1 = 0;
|
||||
if (pathlen != 2)
|
||||
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
|
||||
break;
|
||||
case SC_PATH_TYPE_PATH:
|
||||
apdu.p1 = 0x08;
|
||||
if (pathlen >= 2 && memcmp(path, "\x3F\x00", 2) == 0)
|
||||
{
|
||||
if (pathlen == 2)
|
||||
{
|
||||
/* only 3F00 supplied */
|
||||
apdu.p1 = 0;
|
||||
break;
|
||||
}
|
||||
path += 2;
|
||||
pathlen -= 2;
|
||||
}
|
||||
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);
|
||||
}
|
||||
apdu.lc = pathlen;
|
||||
apdu.data = path;
|
||||
apdu.datalen = pathlen;
|
||||
|
||||
if (file_out != NULL)
|
||||
{
|
||||
apdu.resp = buf;
|
||||
apdu.resplen = sizeof(buf);
|
||||
apdu.le = sizeof(buf) - 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
apdu.resplen = 0;
|
||||
apdu.le = 0;
|
||||
apdu.cse = SC_APDU_CASE_3_SHORT;
|
||||
}
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "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));
|
||||
}
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
SC_TEST_RET(card->ctx, r, "");
|
||||
|
||||
if (apdu.resplen > 0 && apdu.resp[0] != 0x6F) /* Tag 0x6F - FCI */
|
||||
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)
|
||||
r = card->ops->process_fci(card, file, apdu.resp+2, apdu.resp[1]);
|
||||
if (file->sec_attr && file->sec_attr_len == SEC_ATTR_SIZE)
|
||||
set_acl_from_sec_attr(card, file);
|
||||
else
|
||||
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
|
||||
if (r)
|
||||
sc_file_free(file);
|
||||
else
|
||||
{
|
||||
assert(file_out);
|
||||
*file_out = file;
|
||||
}
|
||||
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||
}
|
||||
|
||||
static int rtecp_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 r, send_logout = 0;
|
||||
|
||||
(void)type; /* no warning */
|
||||
assert(card && card->ctx && data);
|
||||
for (;;)
|
||||
{
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT,
|
||||
0x20, 0, ref_qualifier);
|
||||
apdu.lc = data_len;
|
||||
apdu.data = data;
|
||||
apdu.datalen = data_len;
|
||||
apdu.sensitive = 1;
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
if (send_logout++ == 0 && apdu.sw1 == 0x6F && apdu.sw2 == 0x86)
|
||||
{
|
||||
r = sc_logout(card);
|
||||
SC_TEST_RET(card->ctx, r, "Logout failed");
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (apdu.sw1 == 0x63 && apdu.sw2 == 0)
|
||||
{
|
||||
/* Verification failed */
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0, ref_qualifier);
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
}
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
if (r == SC_ERROR_PIN_CODE_INCORRECT && tries_left)
|
||||
*tries_left = (int)(apdu.sw2 & 0x0F);
|
||||
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||
}
|
||||
|
||||
static int rtecp_logout(sc_card_t *card)
|
||||
{
|
||||
sc_apdu_t apdu;
|
||||
int r;
|
||||
|
||||
assert(card && card->ctx);
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x40, 0, 0);
|
||||
apdu.cla = 0x80;
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||
}
|
||||
|
||||
static int rtecp_set_security_env(sc_card_t *card, const sc_security_env_t *env,
|
||||
int se_num)
|
||||
{
|
||||
sc_apdu_t apdu;
|
||||
u8 buf[8], tmp, *p = buf;
|
||||
int r;
|
||||
|
||||
(void)se_num; /* no warning */
|
||||
assert(card && card->ctx && env);
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0);
|
||||
switch (env->operation)
|
||||
{
|
||||
case SC_SEC_OPERATION_DECIPHER:
|
||||
apdu.p2 = 0xB8;
|
||||
break;
|
||||
case SC_SEC_OPERATION_SIGN:
|
||||
apdu.p2 = 0xB6;
|
||||
break;
|
||||
default:
|
||||
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
|
||||
}
|
||||
if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT)
|
||||
{
|
||||
tmp = env->algorithm_ref & 0xFF;
|
||||
sc_asn1_put_tag(0x80, &tmp, sizeof(tmp), p, sizeof(buf) - (p - buf), &p);
|
||||
}
|
||||
if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT && card->ctx->debug >= 4)
|
||||
sc_debug(card->ctx, "%s\n", "SC_SEC_ENV_FILE_REF_PRESENT not supported");
|
||||
if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT)
|
||||
sc_asn1_put_tag(env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC ? 0x83 : 0x84,
|
||||
env->key_ref, env->key_ref_len,
|
||||
p, sizeof(buf) - (p - buf), &p);
|
||||
|
||||
apdu.lc = p - buf;
|
||||
apdu.data = buf;
|
||||
apdu.datalen = p - buf;
|
||||
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||
}
|
||||
|
||||
static int rtecp_rsa_cipher(sc_card_t *card, const u8 *data, size_t data_len,
|
||||
u8 *out, size_t out_len, int sign)
|
||||
{
|
||||
sc_apdu_t apdu;
|
||||
u8 *buf, *buf_out;
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
assert(card && card->ctx && data && out);
|
||||
buf_out = malloc(data_len + 2);
|
||||
buf = malloc(data_len);
|
||||
if (!buf || !buf_out)
|
||||
{
|
||||
free(buf);
|
||||
free(buf_out);
|
||||
SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
for (i = 0; i < data_len; ++i)
|
||||
buf[i] = data[data_len - 1 - i];
|
||||
|
||||
if (sign)
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A);
|
||||
else
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86);
|
||||
apdu.lc = data_len;
|
||||
apdu.data = buf;
|
||||
apdu.datalen = data_len;
|
||||
apdu.sensitive = 1;
|
||||
apdu.resp = buf_out;
|
||||
apdu.resplen = data_len + 2;
|
||||
apdu.le = data_len;
|
||||
if (apdu.lc > 255)
|
||||
apdu.flags |= SC_APDU_FLAGS_CHAINING;
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
if (!sign)
|
||||
{
|
||||
assert(buf);
|
||||
sc_mem_clear(buf, data_len);
|
||||
}
|
||||
assert(buf);
|
||||
free(buf);
|
||||
if (r)
|
||||
sc_error(card->ctx, "APDU transmit failed: %s\n", sc_strerror(r));
|
||||
else
|
||||
{
|
||||
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
|
||||
{
|
||||
assert(buf_out);
|
||||
for (i = 0; i < out_len && i < data_len && i < apdu.resplen; ++i)
|
||||
out[i] = buf_out[data_len - 1 - i];
|
||||
r = (i > 0) ? (int)i : SC_ERROR_INTERNAL;
|
||||
}
|
||||
else
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
}
|
||||
if (!sign)
|
||||
{
|
||||
assert(buf_out);
|
||||
sc_mem_clear(buf_out, data_len + 2);
|
||||
}
|
||||
assert(buf_out);
|
||||
free(buf_out);
|
||||
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||
|
||||
}
|
||||
|
||||
static int rtecp_decipher(sc_card_t *card,
|
||||
const u8 *data, size_t data_len, u8 *out, size_t out_len)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert(card && card->ctx && data && out);
|
||||
/* decipher */
|
||||
r = rtecp_rsa_cipher(card, data, data_len, out, out_len, 0);
|
||||
SC_FUNC_RETURN(card->ctx, 3, r);
|
||||
}
|
||||
|
||||
static int rtecp_compute_signature(sc_card_t *card,
|
||||
const u8 *data, size_t data_len, u8 *out, size_t out_len)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert(card && card->ctx && data && out);
|
||||
/* compute digital signature */
|
||||
r = rtecp_rsa_cipher(card, data, data_len, out, out_len, 1);
|
||||
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||
}
|
||||
|
||||
static int rtecp_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;
|
||||
u8 tmp[2], buf[0x1000], *p = buf;
|
||||
size_t taglen;
|
||||
int r;
|
||||
|
||||
assert(card && card->ctx && newref);
|
||||
if (card->ctx->debug >= 3)
|
||||
sc_debug(card->ctx, "newlen = %u\n", newlen);
|
||||
if (newlen > sizeof(buf) - 2 - sizeof(tmp) - 2 * (sizeof(buf) / 0xFF + 1))
|
||||
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
|
||||
|
||||
if (type == SC_AC_CHV && old && oldlen != 0)
|
||||
{
|
||||
r = sc_verify(card, type, ref_qualifier, old, oldlen, tries_left);
|
||||
SC_TEST_RET(card->ctx, r, "Verify old pin failed");
|
||||
}
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0x01, ref_qualifier);
|
||||
tmp[0] = (newlen >> 8) & 0xFF;
|
||||
tmp[1] = newlen & 0xFF;
|
||||
sc_asn1_put_tag(0x80, tmp, sizeof(tmp), p, sizeof(buf) - (p - buf), &p);
|
||||
r = sc_asn1_put_tag(0xA5, newref, newlen, p, sizeof(buf) - (p - buf), &p);
|
||||
if (r == SC_ERROR_INVALID_ARGUMENTS)
|
||||
{
|
||||
while (newlen)
|
||||
{
|
||||
assert(sizeof(buf) - (p - buf) >= newlen + 2);
|
||||
assert((p - buf) % 0xFF < 0x80);
|
||||
if ((p - buf) % 0xFF % 0x80 + newlen + 2 > 0xFF)
|
||||
taglen = 0xFF - 2 - (p - buf) % 0xFF % 0x80;
|
||||
else
|
||||
taglen = newlen;
|
||||
*p++ = 0xA5;
|
||||
*p++ = (u8)taglen;
|
||||
assert(taglen <= newlen);
|
||||
memcpy(p, newref, taglen);
|
||||
p += taglen;
|
||||
newref += taglen;
|
||||
newlen -= taglen;
|
||||
if (newlen)
|
||||
apdu.flags |= SC_APDU_FLAGS_CHAINING;
|
||||
}
|
||||
}
|
||||
apdu.lc = p - buf;
|
||||
apdu.data = buf;
|
||||
apdu.datalen = p - buf;
|
||||
apdu.sensitive = 1;
|
||||
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
sc_mem_clear(buf, sizeof(buf));
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||
}
|
||||
|
||||
static int rtecp_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)
|
||||
{
|
||||
sc_apdu_t apdu;
|
||||
int r;
|
||||
|
||||
(void)type, (void)puk, (void)puklen, (void)newref, (void)newlen; /* no warning */
|
||||
assert(card && card->ctx);
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2C, 0x03, ref_qualifier);
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||
}
|
||||
|
||||
static int rtecp_create_file(sc_card_t *card, sc_file_t *file)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert(card && card->ctx && file);
|
||||
if (file->sec_attr_len == 0)
|
||||
{
|
||||
r = set_sec_attr_from_acl(card, file);
|
||||
SC_TEST_RET(card->ctx, r, "Set sec_attr from ACL failed");
|
||||
}
|
||||
assert(iso_ops && iso_ops->create_file);
|
||||
r = iso_ops->create_file(card, file);
|
||||
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||
}
|
||||
|
||||
static int rtecp_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 r;
|
||||
|
||||
assert(card && card->ctx && 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;
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
if (apdu.sw1 == 0x6A && apdu.sw2 == 0x82)
|
||||
break; /* Next file not found */
|
||||
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
SC_TEST_RET(card->ctx, r, "");
|
||||
|
||||
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)
|
||||
{
|
||||
memcpy(&buf[len], previd, sizeof(previd));
|
||||
len += sizeof(previd);
|
||||
}
|
||||
|
||||
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_1, 0xA4, 0x03, 0);
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
SC_TEST_RET(card->ctx, r, "");
|
||||
}
|
||||
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, 2, len);
|
||||
}
|
||||
|
||||
static int rtecp_card_ctl(sc_card_t *card, unsigned long request, void *data)
|
||||
{
|
||||
sc_apdu_t apdu;
|
||||
u8 buf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
sc_rtecp_genkey_data_t *genkey_data = data;
|
||||
sc_serial_number_t *serial = data;
|
||||
int r;
|
||||
|
||||
assert(card && card->ctx);
|
||||
switch (request)
|
||||
{
|
||||
case SC_CARDCTL_RTECP_INIT:
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x8A, 0, 0);
|
||||
apdu.cla = 0x80;
|
||||
break;
|
||||
case SC_CARDCTL_RTECP_INIT_END:
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x84, 0x4E, 0x19);
|
||||
apdu.cla = 0x80;
|
||||
break;
|
||||
case SC_CARDCTL_GET_SERIALNR:
|
||||
if (!serial)
|
||||
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x01, 0x81);
|
||||
apdu.resp = buf;
|
||||
apdu.resplen = sizeof(buf);
|
||||
apdu.le = sizeof(buf) - 2;
|
||||
break;
|
||||
case SC_CARDCTL_RTECP_GENERATE_KEY:
|
||||
if (!genkey_data)
|
||||
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x46, 0x80,
|
||||
genkey_data->key_id);
|
||||
apdu.resp = buf;
|
||||
apdu.resplen = sizeof(buf);
|
||||
apdu.le = sizeof(buf) - 2;
|
||||
break;
|
||||
case SC_CARDCTL_LIFECYCLE_SET:
|
||||
if (card->ctx->debug >= 4)
|
||||
sc_debug(card->ctx, "%s\n",
|
||||
"SC_CARDCTL_LIFECYCLE_SET not supported");
|
||||
/* no call sc_error (SC_FUNC_RETURN) */
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
default:
|
||||
if (card->ctx->debug >= 3)
|
||||
sc_debug(card->ctx, "request = 0x%lx\n", request);
|
||||
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NOT_SUPPORTED);
|
||||
}
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
if (!r && request == SC_CARDCTL_RTECP_GENERATE_KEY)
|
||||
{
|
||||
if (genkey_data->modulus_len >= apdu.resplen &&
|
||||
genkey_data->exponent_len >= 3)
|
||||
{
|
||||
memcpy(genkey_data->modulus, apdu.resp, apdu.resplen);
|
||||
genkey_data->modulus_len = apdu.resplen;
|
||||
reverse(genkey_data->modulus, genkey_data->modulus_len);
|
||||
memcpy(genkey_data->exponent, "\x01\x00\x01", 3);
|
||||
genkey_data->exponent_len = 3;
|
||||
}
|
||||
else
|
||||
r = SC_ERROR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
else if (!r && request == SC_CARDCTL_GET_SERIALNR)
|
||||
{
|
||||
if (serial->len >= apdu.resplen)
|
||||
{
|
||||
memcpy(serial->value, apdu.resp, apdu.resplen);
|
||||
serial->len = apdu.resplen;
|
||||
}
|
||||
else
|
||||
r = SC_ERROR_BUFFER_TOO_SMALL;
|
||||
}
|
||||
SC_FUNC_RETURN(card->ctx, 2, r);
|
||||
}
|
||||
|
||||
static int rtecp_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 && file && out && outlen);
|
||||
assert(*outlen >= (size_t)(p - out) + 2);
|
||||
*p++ = 0x6F; /* FCI template */
|
||||
p++; /* for length */
|
||||
|
||||
/* 0x80 - Number of data bytes in the file, excluding structural information */
|
||||
buf[0] = (file->size >> 8) & 0xFF;
|
||||
buf[1] = 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[0] = (file->id >> 8) & 0xFF;
|
||||
buf[1] = 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, 2, 0);
|
||||
}
|
||||
|
||||
struct sc_card_driver * sc_get_rtecp_driver(void)
|
||||
{
|
||||
if (iso_ops == NULL)
|
||||
iso_ops = sc_get_iso7816_driver()->ops;
|
||||
rtecp_ops = *iso_ops;
|
||||
|
||||
rtecp_ops.match_card = rtecp_match_card;
|
||||
rtecp_ops.init = rtecp_init;
|
||||
rtecp_ops.finish = NULL;
|
||||
/* read_binary */
|
||||
rtecp_ops.write_binary = NULL;
|
||||
/* update_binary */
|
||||
rtecp_ops.erase_binary = NULL;
|
||||
rtecp_ops.read_record = NULL;
|
||||
rtecp_ops.write_record = NULL;
|
||||
rtecp_ops.append_record = NULL;
|
||||
rtecp_ops.update_record = NULL;
|
||||
rtecp_ops.select_file = rtecp_select_file;
|
||||
rtecp_ops.get_response = NULL;
|
||||
/* get_challenge */
|
||||
rtecp_ops.verify = rtecp_verify;
|
||||
rtecp_ops.logout = rtecp_logout;
|
||||
/* restore_security_env */
|
||||
rtecp_ops.set_security_env = rtecp_set_security_env;
|
||||
rtecp_ops.decipher = rtecp_decipher;
|
||||
rtecp_ops.compute_signature = rtecp_compute_signature;
|
||||
rtecp_ops.change_reference_data = rtecp_change_reference_data;
|
||||
rtecp_ops.reset_retry_counter = rtecp_reset_retry_counter;
|
||||
rtecp_ops.create_file = rtecp_create_file;
|
||||
/* delete_file */
|
||||
rtecp_ops.list_files = rtecp_list_files;
|
||||
/* check_sw */
|
||||
rtecp_ops.card_ctl = rtecp_card_ctl;
|
||||
/* process_fci */
|
||||
rtecp_ops.construct_fci = rtecp_construct_fci;
|
||||
rtecp_ops.pin_cmd = NULL;
|
||||
rtecp_ops.get_data = NULL;
|
||||
rtecp_ops.put_data = NULL;
|
||||
rtecp_ops.delete_record = NULL;
|
||||
|
||||
return &rtecp_drv;
|
||||
}
|
||||
|
|
@ -167,6 +167,14 @@ enum {
|
|||
SC_CARDCTL_ENTERSAFE_WRITE_KEY,
|
||||
SC_CARDCTL_ENTERSAFE_GENERATE_KEY,
|
||||
SC_CARDCTL_ENTERSAFE_PREINSTALL_KEYS,
|
||||
|
||||
/*
|
||||
* Rutoken ECP specific calls
|
||||
*/
|
||||
SC_CARDCTL_RTECP_BASE = _CTL_PREFIX('R', 'T', 'E'),
|
||||
SC_CARDCTL_RTECP_INIT,
|
||||
SC_CARDCTL_RTECP_INIT_END,
|
||||
SC_CARDCTL_RTECP_GENERATE_KEY,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -642,6 +650,17 @@ typedef struct sc_entersafe_gen_key_data_st {
|
|||
|
||||
#pragma pack(pop)
|
||||
|
||||
/*
|
||||
* Rutoken ECP stuff
|
||||
*/
|
||||
typedef struct sc_rtecp_genkey_data {
|
||||
unsigned int key_id;
|
||||
unsigned char *exponent;
|
||||
size_t exponent_len;
|
||||
unsigned char *modulus;
|
||||
size_t modulus_len;
|
||||
} sc_rtecp_genkey_data_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -147,7 +147,6 @@ enum {
|
|||
SC_CARD_TYPE_ENTERSAFE_FTCOS_PK_01C,
|
||||
};
|
||||
|
||||
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);
|
||||
|
@ -172,6 +171,8 @@ extern sc_card_driver_t *sc_get_acos5_driver(void);
|
|||
extern sc_card_driver_t *sc_get_asepcos_driver(void);
|
||||
extern sc_card_driver_t *sc_get_akis_driver(void);
|
||||
extern sc_card_driver_t *sc_get_entersafe_driver(void);
|
||||
extern sc_card_driver_t *sc_get_rutoken_driver(void);
|
||||
extern sc_card_driver_t *sc_get_rtecp_driver(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -51,7 +51,6 @@ 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 },
|
||||
|
@ -84,6 +83,8 @@ static const struct _sc_driver_entry internal_card_drivers[] = {
|
|||
#ifdef ENABLE_OPENSSL
|
||||
{ "entersafe",(void *(*)(void)) sc_get_entersafe_driver },
|
||||
#endif
|
||||
{ "rutoken", (void *(*)(void)) sc_get_rutoken_driver },
|
||||
{ "rutoken_ecp",(void *(*)(void)) sc_get_rtecp_driver },
|
||||
/* The default driver should be last, as it handles all the
|
||||
* unrecognized cards. */
|
||||
{ "default", (void *(*)(void)) sc_get_default_driver },
|
||||
|
|
|
@ -23,7 +23,8 @@ dist_pkgdata_DATA = \
|
|||
muscle.profile \
|
||||
rutoken.profile \
|
||||
asepcos.profile \
|
||||
entersafe.profile
|
||||
entersafe.profile \
|
||||
rutoken_ecp.profile
|
||||
|
||||
AM_CPPFLAGS = -DSC_PKCS15_PROFILE_DIRECTORY=\"$(pkgdatadir)\"
|
||||
AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(LTLIB_CFLAGS)
|
||||
|
@ -35,7 +36,7 @@ libpkcs15init_la_SOURCES = \
|
|||
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-rutoken.c \
|
||||
pkcs15-entersafe.c \
|
||||
pkcs15-entersafe.c pkcs15-rtecp.c \
|
||||
pkcs15init.exports
|
||||
if WIN32
|
||||
libpkcs15init_la_SOURCES += versioninfo.rc
|
||||
|
|
|
@ -10,8 +10,8 @@ OBJECTS = pkcs15-lib.obj profile.obj keycache.obj \
|
|||
pkcs15-cardos.obj pkcs15-jcop.obj pkcs15-starcos.obj \
|
||||
pkcs15-oberthur.obj pkcs15-setcos.obj pkcs15-incrypto34.obj \
|
||||
pkcs15-muscle.obj pkcs15-asepcos.obj pkcs15-rutoken.obj \
|
||||
pkcs15-entersafe.obj \
|
||||
versioninfo.res
|
||||
pkcs15-entersafe.obj pkcs15-rtecp.obj \
|
||||
versioninfo.res
|
||||
|
||||
all: install-headers $(TARGET)
|
||||
|
||||
|
|
|
@ -403,6 +403,7 @@ 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);
|
||||
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_entersafe_ops(void);
|
||||
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_rtecp_ops(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -164,6 +164,7 @@ static struct profile_operations {
|
|||
{ "muscle", (void*) sc_pkcs15init_get_muscle_ops },
|
||||
{ "asepcos", (void*) sc_pkcs15init_get_asepcos_ops },
|
||||
{ "entersafe",(void*) sc_pkcs15init_get_entersafe_ops },
|
||||
{ "rutoken_ecp", (void *) sc_pkcs15init_get_rtecp_ops },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,502 @@
|
|||
/*
|
||||
* pkcs15-rtecp.c: Rutoken ECP specific operation for PKCS15 initialization
|
||||
*
|
||||
* Copyright (C) 2009 Aleksey Samsonov <samsonov@guardant.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 <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.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 RTECP_SO_PIN_REF 1
|
||||
#define RTECP_USER_PIN_REF 2
|
||||
|
||||
/*
|
||||
* Erase everything that's on the card
|
||||
*/
|
||||
static int rtecp_erase(sc_profile_t *profile, sc_card_t *card)
|
||||
{
|
||||
if (!profile || !card)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
return sc_card_ctl(card, SC_CARDCTL_RTECP_INIT, NULL);
|
||||
}
|
||||
|
||||
static int create_sysdf(sc_profile_t *profile, sc_card_t *card, const char *name)
|
||||
{
|
||||
sc_file_t *file;
|
||||
sc_path_t path;
|
||||
int r;
|
||||
|
||||
assert(profile && card && card->ctx && name);
|
||||
r = sc_profile_get_file(profile, name, &file);
|
||||
if (r == SC_SUCCESS)
|
||||
{
|
||||
assert(file);
|
||||
path = file->path;
|
||||
assert(path.len > 2);
|
||||
if (path.len > 2)
|
||||
path.len -= 2;
|
||||
r = sc_select_file(card, &path, NULL);
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_file_add_acl_entry(file, SC_AC_OP_CREATE,
|
||||
SC_AC_CHV, RTECP_USER_PIN_REF);
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_file_add_acl_entry(file, SC_AC_OP_DELETE,
|
||||
SC_AC_NEVER, SC_AC_KEY_REF_NONE);
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_create_file(card, file);
|
||||
assert(file);
|
||||
sc_file_free(file);
|
||||
}
|
||||
if (r && card->ctx->debug >= 2)
|
||||
sc_debug(card->ctx, "Create %s failed: %s\n", name, sc_strerror(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Card-specific initialization of PKCS15 meta-information
|
||||
*/
|
||||
static int rtecp_init(sc_profile_t *profile, sc_card_t *card)
|
||||
{
|
||||
sc_file_t *file;
|
||||
int r;
|
||||
|
||||
if (!profile || !card || !card->ctx)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
|
||||
r = sc_profile_get_file(profile, "MF", &file);
|
||||
SC_TEST_RET(card->ctx, r, "Get MF info failed");
|
||||
assert(file);
|
||||
r = sc_create_file(card, file);
|
||||
assert(file);
|
||||
sc_file_free(file);
|
||||
SC_TEST_RET(card->ctx, r, "Create MF failed");
|
||||
|
||||
r = sc_profile_get_file(profile, "DIR", &file);
|
||||
SC_TEST_RET(card->ctx, r, "Get DIR file info failed");
|
||||
assert(file);
|
||||
r = sc_create_file(card, file);
|
||||
assert(file);
|
||||
sc_file_free(file);
|
||||
SC_TEST_RET(card->ctx, r, "Create DIR file failed");
|
||||
|
||||
create_sysdf(profile, card, "Sys-DF");
|
||||
create_sysdf(profile, card, "SysKey-DF");
|
||||
create_sysdf(profile, card, "PuKey-DF");
|
||||
create_sysdf(profile, card, "PrKey-DF");
|
||||
create_sysdf(profile, card, "SKey-DF");
|
||||
create_sysdf(profile, card, "Cer-DF");
|
||||
create_sysdf(profile, card, "LCHV-DF");
|
||||
|
||||
return sc_select_file(card, sc_get_mf_path(), NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a DF
|
||||
*/
|
||||
static int rtecp_create_dir(sc_profile_t *profile, sc_card_t *card, sc_file_t *df)
|
||||
{
|
||||
if (!profile || !card || !df)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
return sc_create_file(card, df);
|
||||
}
|
||||
|
||||
/*
|
||||
* Select a PIN reference
|
||||
*/
|
||||
static int rtecp_select_pin_reference(sc_profile_t *profile, sc_card_t *card,
|
||||
sc_pkcs15_pin_info_t *pin_info)
|
||||
{
|
||||
if (!profile || !card || !card->ctx || !pin_info)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
|
||||
if (pin_info->reference > 2)
|
||||
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NOT_SUPPORTED);
|
||||
if (pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN)
|
||||
pin_info->reference = RTECP_SO_PIN_REF;
|
||||
else
|
||||
pin_info->reference = RTECP_USER_PIN_REF;
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a PIN object within the given DF
|
||||
*/
|
||||
static int rtecp_create_pin(sc_profile_t *profile, sc_card_t *card,
|
||||
sc_file_t *df, sc_pkcs15_object_t *pin_obj,
|
||||
const unsigned char *pin, size_t pin_len,
|
||||
const unsigned char *puk, size_t puk_len)
|
||||
{
|
||||
sc_pkcs15_pin_info_t *pin_info;
|
||||
sc_file_t *file;
|
||||
/* GCHV min-length Flags Attempts Reserve */
|
||||
unsigned char prop[] = { 0x01, '?', 0x01, 0xFF, 0, 0 };
|
||||
/* AccessMode Unblock Change Delete */
|
||||
unsigned char sec[15] = { 0x43, RTECP_SO_PIN_REF, '?', 0, 0, 0, 0, 0xFF };
|
||||
int r;
|
||||
|
||||
(void)puk; /* no warning */
|
||||
if (!profile || !card || !card->ctx || !df || !pin_obj || !pin_obj->data
|
||||
|| !pin || !pin_len)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
|
||||
SC_FUNC_CALLED(card->ctx, 1);
|
||||
if (puk_len != 0)
|
||||
{
|
||||
sc_error(card->ctx, "Do not enter User unblocking PIN (PUK): %s\n",
|
||||
sc_strerror(SC_ERROR_NOT_SUPPORTED));
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
pin_info = (sc_pkcs15_pin_info_t *)pin_obj->data;
|
||||
if (pin_info->reference != RTECP_SO_PIN_REF
|
||||
&& pin_info->reference != RTECP_USER_PIN_REF)
|
||||
{
|
||||
sc_debug(card->ctx, "PIN reference %i not found in standard"
|
||||
" (Rutoken ECP) PINs\n", pin_info->reference);
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
file = sc_file_new();
|
||||
if (!file)
|
||||
SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY);
|
||||
file->id = pin_info->reference;
|
||||
file->size = pin_len;
|
||||
assert(sizeof(sec)/sizeof(sec[0]) > 2);
|
||||
sec[2] = (unsigned char)pin_info->reference;
|
||||
r = sc_file_set_sec_attr(file, sec, sizeof(sec));
|
||||
if (r == SC_SUCCESS)
|
||||
{
|
||||
assert(sizeof(prop)/sizeof(prop[0]) > 1);
|
||||
prop[1] = (unsigned char)pin_info->min_length;
|
||||
r = sc_file_set_prop_attr(file, prop, sizeof(prop));
|
||||
}
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_file_set_type_attr(file, (const u8*)"\x10\x00", 2);
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_create_file(card, file);
|
||||
sc_file_free(file);
|
||||
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_change_reference_data(card, pin_info->type, pin_info->reference,
|
||||
NULL, 0, pin, pin_len, NULL);
|
||||
SC_FUNC_RETURN(card->ctx, 1, r);
|
||||
}
|
||||
|
||||
/*
|
||||
* Select a reference for a private key object
|
||||
*/
|
||||
static int rtecp_select_key_reference(sc_profile_t *profile,
|
||||
sc_card_t *card, sc_pkcs15_prkey_info_t *key_info)
|
||||
{
|
||||
sc_file_t *df;
|
||||
int r;
|
||||
|
||||
if (!profile || !card || !card->ctx || !key_info)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
|
||||
if (key_info->key_reference <= 0)
|
||||
key_info->key_reference = 1;
|
||||
else if (key_info->key_reference > 0xFF)
|
||||
return SC_ERROR_TOO_MANY_OBJECTS;
|
||||
|
||||
r = sc_profile_get_file(profile, "PrKey-DF", &df);
|
||||
SC_TEST_RET(card->ctx, r, "Get PrKey-DF info failed");
|
||||
assert(df);
|
||||
key_info->path = df->path;
|
||||
sc_file_free(df);
|
||||
r = sc_append_file_id(&key_info->path, key_info->key_reference);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an empty key object
|
||||
*/
|
||||
static int rtecp_create_key(sc_profile_t *profile, sc_card_t *card,
|
||||
sc_pkcs15_object_t *obj)
|
||||
{
|
||||
/* RSA_PRkey/ for Miller-
|
||||
* RSA_PUBkey Rabin test Attempts Reserve */
|
||||
const unsigned char prkey_prop[] = { 0x23, 0x1F, 0, 0xFF, 0, 0 };
|
||||
const unsigned char pbkey_prop[] = { 0x33, 0x1F, 0, 0xFF, 0, 0 };
|
||||
/* AccessMode - Update Use - - - Delete */
|
||||
unsigned char prkey_sec[15] = { 0x46, 0, '?', '?', 0, 0, 0, '?' };
|
||||
unsigned char pbkey_sec[15] = { 0x46, 0, '?', 0, 0, 0, 0, '?' };
|
||||
unsigned char auth_id;
|
||||
sc_pkcs15_prkey_info_t *key_info;
|
||||
sc_file_t *file;
|
||||
int r;
|
||||
|
||||
if (!profile || !card || !card->ctx || !obj || !obj->data)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
|
||||
SC_FUNC_CALLED(card->ctx, 1);
|
||||
if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA)
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
if (obj->auth_id.len != 1)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
auth_id = obj->auth_id.value[0];
|
||||
|
||||
key_info = (sc_pkcs15_prkey_info_t *)obj->data;
|
||||
assert(key_info);
|
||||
if (key_info->modulus_length % 128 != 0)
|
||||
{
|
||||
sc_error(card->ctx, "Unsupported key size %u\n",
|
||||
key_info->modulus_length);
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
|
||||
r = sc_profile_get_file(profile, "PKCS15-AppDF", &file);
|
||||
SC_TEST_RET(card->ctx, r, "Get PKCS15-AppDF info failed");
|
||||
r = sc_file_add_acl_entry(file, SC_AC_OP_CREATE, SC_AC_CHV, auth_id);
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_pkcs15init_authenticate(profile, card, file, SC_AC_OP_CREATE);
|
||||
assert(file);
|
||||
sc_file_free(file);
|
||||
SC_TEST_RET(card->ctx, r, "Authenticate failed");
|
||||
|
||||
file = sc_file_new();
|
||||
if (!file)
|
||||
SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY);
|
||||
file->id = key_info->key_reference;
|
||||
r = sc_file_set_type_attr(file, (const u8*)"\x10\x00", 2);
|
||||
/* private key file */
|
||||
file->size = key_info->modulus_length / 8 / 2 * 5 + 8;
|
||||
if (r == SC_SUCCESS)
|
||||
{
|
||||
assert(sizeof(prkey_sec)/sizeof(prkey_sec[0]) > 7);
|
||||
prkey_sec[2] = auth_id;
|
||||
prkey_sec[3] = auth_id;
|
||||
prkey_sec[7] = auth_id;
|
||||
r = sc_file_set_sec_attr(file, prkey_sec, sizeof(prkey_sec));
|
||||
}
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_file_set_prop_attr(file, prkey_prop, sizeof(prkey_prop));
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_create_file(card, file);
|
||||
/* public key file */
|
||||
file->size = key_info->modulus_length / 8 / 2 * 3;
|
||||
if (r == SC_SUCCESS)
|
||||
{
|
||||
assert(sizeof(pbkey_sec)/sizeof(pbkey_sec[0]) > 7);
|
||||
pbkey_sec[2] = auth_id;
|
||||
pbkey_sec[7] = auth_id;
|
||||
r = sc_file_set_sec_attr(file, pbkey_sec, sizeof(pbkey_sec));
|
||||
}
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_file_set_prop_attr(file, pbkey_prop, sizeof(pbkey_prop));
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_create_file(card, file);
|
||||
assert(file);
|
||||
sc_file_free(file);
|
||||
SC_FUNC_RETURN(card->ctx, 1, r);
|
||||
}
|
||||
|
||||
/*
|
||||
* Store a key on the card
|
||||
*/
|
||||
static int rtecp_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;
|
||||
sc_file_t *pukey_df;
|
||||
sc_path_t path;
|
||||
unsigned char *buf;
|
||||
size_t len, i;
|
||||
int r;
|
||||
|
||||
if (!profile || !card || !card->ctx || !obj || !obj->data || !key)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
|
||||
SC_FUNC_CALLED(card->ctx, 1);
|
||||
if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA || key->algorithm != SC_ALGORITHM_RSA)
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
|
||||
key_info = (sc_pkcs15_prkey_info_t *)obj->data;
|
||||
assert(key_info);
|
||||
|
||||
assert(key_info->modulus_length % 128 == 0);
|
||||
len = key_info->modulus_length / 8 / 2;
|
||||
if (!key->u.rsa.p.data || !key->u.rsa.q.data || !key->u.rsa.iqmp.data
|
||||
|| !key->u.rsa.dmp1.data || !key->u.rsa.dmq1.data
|
||||
|| !key->u.rsa.modulus.data || !key->u.rsa.exponent.data
|
||||
|| key->u.rsa.p.len != len || key->u.rsa.q.len != len
|
||||
|| key->u.rsa.iqmp.len != len || key->u.rsa.dmp1.len != len
|
||||
|| key->u.rsa.dmq1.len != len || key->u.rsa.modulus.len != 2*len
|
||||
|| key->u.rsa.exponent.len > len || key->u.rsa.exponent.len == 0)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
|
||||
buf = calloc(1, len * 5 + 8);
|
||||
if (!buf)
|
||||
SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY);
|
||||
assert(key->u.rsa.p.data && key->u.rsa.q.data && key->u.rsa.iqmp.data
|
||||
&& key->u.rsa.dmp1.data && key->u.rsa.dmq1.data
|
||||
&& key->u.rsa.p.len == len && key->u.rsa.q.len == len
|
||||
&& key->u.rsa.iqmp.len == len
|
||||
&& key->u.rsa.dmp1.len == len
|
||||
&& key->u.rsa.dmq1.len == len);
|
||||
/* p */
|
||||
for (i = 0; i < len; ++i)
|
||||
buf[i] = key->u.rsa.p.data[len - 1 - i];
|
||||
/* q */
|
||||
for (i = 0; i < len; ++i)
|
||||
buf[len + 4 + i] = key->u.rsa.q.data[len - 1 - i];
|
||||
/* iqmp */
|
||||
for (i = 0; i < len; ++i)
|
||||
buf[len + 4 + len + 4 + i] = key->u.rsa.iqmp.data[len - 1 - i];
|
||||
/* dmp1 */
|
||||
for (i = 0; i < len; ++i)
|
||||
buf[len + 4 + len + 4 + len + i] = key->u.rsa.dmp1.data[len - 1 - i];
|
||||
/* dmq1 */
|
||||
for (i = 0; i < len; ++i)
|
||||
buf[len * 4 + 8 + i] = key->u.rsa.dmq1.data[len - 1 - i];
|
||||
|
||||
path = key_info->path;
|
||||
r = sc_select_file(card, &path, NULL);
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_change_reference_data(card, 0, 0, NULL, 0, buf, len*5 + 8, NULL);
|
||||
assert(buf);
|
||||
sc_mem_clear(buf, len * 5 + 8);
|
||||
/* store public key */
|
||||
assert(key->u.rsa.modulus.data && key->u.rsa.exponent.data
|
||||
&& key->u.rsa.modulus.len == 2*len
|
||||
&& key->u.rsa.exponent.len <= len
|
||||
&& key->u.rsa.exponent.len > 0);
|
||||
/* modulus */
|
||||
for (i = 0; i < 2*len; ++i)
|
||||
buf[i] = key->u.rsa.modulus.data[2*len - 1 - i];
|
||||
/* exponent */
|
||||
for (i = 0; i < key->u.rsa.exponent.len && i < len; ++i)
|
||||
buf[2*len+i] = key->u.rsa.exponent.data[key->u.rsa.exponent.len - 1 - i];
|
||||
if (r == SC_SUCCESS)
|
||||
{
|
||||
r = sc_profile_get_file(profile, "PuKey-DF", &pukey_df);
|
||||
if (r == SC_SUCCESS)
|
||||
{
|
||||
assert(pukey_df);
|
||||
path = pukey_df->path;
|
||||
r = sc_append_file_id(&path, key_info->key_reference);
|
||||
sc_file_free(pukey_df);
|
||||
}
|
||||
else if (card->ctx->debug >= 2)
|
||||
sc_debug(card->ctx, "%s\n", "Get PuKey-DF info failed");
|
||||
}
|
||||
if (r == SC_SUCCESS)
|
||||
{
|
||||
r = sc_select_file(card, &path, NULL);
|
||||
if (r == SC_SUCCESS)
|
||||
r = sc_change_reference_data(card, 0, 0, NULL, 0,
|
||||
buf, len*3, NULL);
|
||||
if (r && card->ctx->debug >= 2)
|
||||
sc_debug(card->ctx, "%s\n", "Store public key failed");
|
||||
}
|
||||
assert(buf);
|
||||
free(buf);
|
||||
SC_FUNC_RETURN(card->ctx, 1, r);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate key
|
||||
*/
|
||||
static int rtecp_generate_key(sc_profile_t *profile, sc_card_t *card,
|
||||
sc_pkcs15_object_t *obj, sc_pkcs15_pubkey_t *pubkey)
|
||||
{
|
||||
sc_pkcs15_prkey_info_t *key_info;
|
||||
sc_rtecp_genkey_data_t data;
|
||||
int r;
|
||||
|
||||
if (!profile || !card || !card->ctx || !obj || !obj->data || !pubkey)
|
||||
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;
|
||||
assert(key_info);
|
||||
data.key_id = key_info->key_reference;
|
||||
assert(data.key_id != 0);
|
||||
assert(key_info->modulus_length % 128 == 0);
|
||||
data.modulus_len = key_info->modulus_length / 8;
|
||||
data.modulus = calloc(1, data.modulus_len);
|
||||
data.exponent_len = key_info->modulus_length / 8 / 2;
|
||||
data.exponent = calloc(1, data.exponent_len);
|
||||
if (!data.modulus || !data.exponent)
|
||||
{
|
||||
free(data.modulus);
|
||||
free(data.exponent);
|
||||
SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
r = sc_card_ctl(card, SC_CARDCTL_RTECP_GENERATE_KEY, &data);
|
||||
if (r == SC_SUCCESS)
|
||||
{
|
||||
assert(pubkey);
|
||||
pubkey->algorithm = SC_ALGORITHM_RSA;
|
||||
pubkey->u.rsa.modulus.data = data.modulus;
|
||||
pubkey->u.rsa.modulus.len = data.modulus_len;
|
||||
pubkey->u.rsa.exponent.data = data.exponent;
|
||||
pubkey->u.rsa.exponent.len = data.exponent_len;
|
||||
}
|
||||
SC_FUNC_RETURN(card->ctx, 1, r);
|
||||
}
|
||||
|
||||
/*
|
||||
* Finalize card
|
||||
* Ends the initialization phase of the smart card/token
|
||||
*/
|
||||
static int rtecp_finalize(sc_card_t *card)
|
||||
{
|
||||
if (!card)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
return sc_card_ctl(card, SC_CARDCTL_RTECP_INIT_END, NULL);
|
||||
}
|
||||
|
||||
static struct sc_pkcs15init_operations sc_pkcs15init_rtecp_operations = {
|
||||
rtecp_erase, /* erase_card */
|
||||
rtecp_init, /* init_card */
|
||||
rtecp_create_dir, /* create_dir */
|
||||
NULL, /* create_domain */
|
||||
rtecp_select_pin_reference, /* select_pin_reference */
|
||||
rtecp_create_pin, /* create_pin */
|
||||
rtecp_select_key_reference, /* select_key_reference */
|
||||
rtecp_create_key, /* create_key */
|
||||
rtecp_store_key, /* store_key */
|
||||
rtecp_generate_key, /* generate_key */
|
||||
NULL, /* encode_private_key */
|
||||
NULL, /* encode_public_key */
|
||||
rtecp_finalize, /* finalize_card */
|
||||
/* Old-style API */
|
||||
NULL, /* init_app */
|
||||
NULL, /* new_pin */
|
||||
NULL, /* new_key */
|
||||
NULL, /* new_file */
|
||||
NULL, /* old_generate_key */
|
||||
NULL /* delete_object */
|
||||
};
|
||||
|
||||
struct sc_pkcs15init_operations * sc_pkcs15init_get_rtecp_ops(void)
|
||||
{
|
||||
return &sc_pkcs15init_rtecp_operations;
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ sc_pkcs15init_get_muscle_ops
|
|||
sc_pkcs15init_get_oberthur_ops
|
||||
sc_pkcs15init_get_pin_info
|
||||
sc_pkcs15init_get_rutoken_ops
|
||||
sc_pkcs15init_get_rtecp_ops
|
||||
sc_pkcs15init_get_serial
|
||||
sc_pkcs15init_get_setcos_ops
|
||||
sc_pkcs15init_get_starcos_ops
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
#
|
||||
# PKCS15 profile, generic information.
|
||||
# This profile is loaded before any card specific profile.
|
||||
#
|
||||
|
||||
cardinfo {
|
||||
label = "Rutoken ECP";
|
||||
manufacturer = "Aktiv Co.";
|
||||
|
||||
max-pin-length = 32;
|
||||
min-pin-length = 1;
|
||||
pin-encoding = ascii-numeric;
|
||||
}
|
||||
|
||||
#
|
||||
# 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 {
|
||||
macros {
|
||||
ti-size = 128;
|
||||
odf-size = 128;
|
||||
aodf-size = 256;
|
||||
dodf-size = 2048;
|
||||
cdf-size = 2048;
|
||||
prkdf-size = 2048;
|
||||
pukdf-size = 2048;
|
||||
}
|
||||
}
|
||||
|
||||
# Define reasonable limits for PINs and PUK
|
||||
# Note that we do not set a file path or reference
|
||||
# for the user pin; that is done dynamically.
|
||||
PIN user-pin {
|
||||
auth-id = 2;
|
||||
reference = 2;
|
||||
attempts = 2;
|
||||
min-length = 8;
|
||||
max-length = 32;
|
||||
flags = case-sensitive, local, initialized;
|
||||
}
|
||||
PIN user-puk {
|
||||
min-length = 0;
|
||||
max-length = 0;
|
||||
}
|
||||
|
||||
PIN so-pin {
|
||||
auth-id = 1;
|
||||
reference = 1;
|
||||
attempts = 2;
|
||||
min-length = 8;
|
||||
max-length = 32;
|
||||
flags = case-sensitive, local, initialized, soPin;
|
||||
}
|
||||
PIN so-puk {
|
||||
min-length = 0;
|
||||
max-length = 0;
|
||||
}
|
||||
|
||||
filesystem {
|
||||
|
||||
DF MF {
|
||||
path = 3F00;
|
||||
type = DF;
|
||||
acl = *=NEVER, SELECT=NONE, DELETE=NEVER, CREATE=CHV2, READ=NONE;
|
||||
|
||||
DF Sys-DF {
|
||||
file-id = 1000;
|
||||
|
||||
DF SysKey-DF {
|
||||
file-id = 1000;
|
||||
|
||||
DF PuKey-DF {
|
||||
file-id = 6001;
|
||||
}
|
||||
|
||||
DF PrKey-DF {
|
||||
file-id = 6002;
|
||||
}
|
||||
|
||||
DF SKey-DF {
|
||||
file-id = 6003;
|
||||
}
|
||||
|
||||
DF Cer-DF {
|
||||
file-id = 6004;
|
||||
}
|
||||
|
||||
DF LCHV-DF {
|
||||
file-id = 6005;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EF DIR {
|
||||
type = EF;
|
||||
file-id = 2F00;
|
||||
size = 128;
|
||||
acl = *=NEVER, READ=NONE, UPDATE=CHV1, WRITE=CHV1, DELETE=CHV1;
|
||||
}
|
||||
|
||||
# Here comes the application DF
|
||||
DF PKCS15-AppDF {
|
||||
type = DF;
|
||||
file-id = 5000;
|
||||
acl = *=NONE, DELETE=CHV2;
|
||||
# acl = *=NEVER, SELECT=NONE, DELETE=CHV2, CREATE=CHV2, READ=NONE;
|
||||
|
||||
EF PKCS15-ODF {
|
||||
file-id = 5031;
|
||||
size = $odf-size;
|
||||
acl = *=NONE, DELETE=$SOPIN;
|
||||
}
|
||||
|
||||
EF PKCS15-TokenInfo {
|
||||
file-id = 5032;
|
||||
size = $ti-size;
|
||||
acl = *=NONE, DELETE=CHV2;
|
||||
}
|
||||
|
||||
EF PKCS15-AODF {
|
||||
file-id = 6005;
|
||||
size = $aodf-size;
|
||||
acl = *=NEVER, READ=NONE, UPDATE=$SOPIN, WRITE=$SOPIN, DELETE=$SOPIN;
|
||||
}
|
||||
|
||||
EF PKCS15-PrKDF {
|
||||
file-id = 6002;
|
||||
size = $prkdf-size;
|
||||
acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
|
||||
}
|
||||
|
||||
EF PKCS15-PuKDF {
|
||||
file-id = 6001;
|
||||
size = $pukdf-size;
|
||||
acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
|
||||
}
|
||||
|
||||
EF PKCS15-CDF {
|
||||
file-id = 6004;
|
||||
size = $cdf-size;
|
||||
acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
|
||||
}
|
||||
|
||||
EF PKCS15-DODF {
|
||||
file-id = 6006;
|
||||
size = $dodf-size;
|
||||
acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
|
||||
}
|
||||
|
||||
# This template defines files for keys, certificates etc.
|
||||
#
|
||||
# When instantiating the template, each file id will be
|
||||
# combined with the last octet of the object's pkcs15 id
|
||||
# to form a unique file ID.
|
||||
template key-domain {
|
||||
EF private-key {
|
||||
file-id = 0100;
|
||||
structure = transparent;
|
||||
acl = *=NEVER, READ=$PIN, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
|
||||
}
|
||||
|
||||
EF public-key {
|
||||
file-id = 0200;
|
||||
structure = transparent;
|
||||
acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
|
||||
}
|
||||
|
||||
# Certificate template
|
||||
EF certificate {
|
||||
file-id = 0300;
|
||||
structure = transparent;
|
||||
acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
|
||||
}
|
||||
|
||||
# data objects are stored in transparent EFs.
|
||||
EF data {
|
||||
file-id = 0400;
|
||||
structure = transparent;
|
||||
acl = *=NEVER, READ=NONE, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
|
||||
}
|
||||
|
||||
# private data objects are stored in transparent EFs.
|
||||
EF privdata {
|
||||
file-id = 0500;
|
||||
structure = transparent;
|
||||
acl = *=NEVER, READ=$PIN, UPDATE=$PIN, WRITE=$PIN, DELETE=$PIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue