add support for cardos m4.2 (still experimental)
git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@2793 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
parent
e1d7384e8a
commit
f5902e4f0c
|
@ -2,6 +2,7 @@
|
|||
* card-etoken.c: Support for Siemens CardOS based cards and tokens
|
||||
* (for example Aladdin eToken PRO, Eutron CryptoIdentity IT-SEC)
|
||||
*
|
||||
* Copyright (c) 2005 Nils Larsch <nils@larsch.net>
|
||||
* Copyright (C) 2002 Andreas Jellinghaus <aj@dungeon.inka.de>
|
||||
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
|
||||
*
|
||||
|
@ -25,6 +26,8 @@
|
|||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <opensc/asn1.h>
|
||||
|
||||
/* andreas says: hm, my card only works for small payloads */
|
||||
/* comment by okir: one of the examples in the developer guide
|
||||
* also talks about copying data in chunks of 128.
|
||||
|
@ -48,8 +51,8 @@ static struct sc_atr_table etoken_atrs[] = {
|
|||
{ "3b:f2:98:00:ff:c1:10:31:fe:55:c8:03:15", NULL, NULL, SC_CARD_TYPE_ETOKEN_GENERIC, 0, NULL },
|
||||
/* 4.01a */
|
||||
{ "3b:f2:98:00:ff:c1:10:31:fe:55:c8:04:12", NULL, NULL, SC_CARD_TYPE_ETOKEN_GENERIC, 0, NULL },
|
||||
/* aladdin etoken pro 64 - aj: not compatible :/ */
|
||||
/* { "3b f2 18 00 ff c1 0a 31 fe 55 c8 06 8a", NULL, NULL, SC_CARD_TYPE_ETOKEN_GENERIC, 0, NULL }, */
|
||||
/* M4.2 */
|
||||
{ "3b:f2:18:00:ff:c1:0a:31:fe:55:c8:06:8a", NULL, NULL, SC_CARD_TYPE_CARDOS_M4_2, 0, NULL },
|
||||
/* Italian eID card, postecert */
|
||||
{ "3b:e9:00:ff:c1:10:31:fe:55:00:64:05:00:c8:02:31:80:00:47", NULL, NULL, SC_CARD_TYPE_ETOKEN_GENERIC, 0, NULL },
|
||||
/* Italian eID card, infocamere */
|
||||
|
@ -75,6 +78,42 @@ static int etoken_match_card(sc_card_t *card)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int cardos_have_2048bit_package(sc_card_t *card)
|
||||
{
|
||||
sc_apdu_t apdu;
|
||||
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
int r;
|
||||
const u8 *p = rbuf, *q;
|
||||
size_t len, tlen = 0, ilen = 0;
|
||||
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x88);
|
||||
apdu.resp = rbuf;
|
||||
apdu.resplen = sizeof(rbuf);
|
||||
apdu.lc = 0;
|
||||
apdu.le = 256;
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
|
||||
if ((len = apdu.resplen) == 0)
|
||||
/* looks like no package has been installed */
|
||||
return 0;
|
||||
|
||||
while (len != 0) {
|
||||
p = sc_asn1_find_tag(card->ctx, p, len, 0xe1, &tlen);
|
||||
if (p == NULL)
|
||||
return 0;
|
||||
q = sc_asn1_find_tag(card->ctx, p, tlen, 0x01, &ilen);
|
||||
if (q == NULL || ilen != 4)
|
||||
return 0;
|
||||
if (q[0] == 0x1c)
|
||||
return 1;
|
||||
p += tlen;
|
||||
len -= tlen + 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int etoken_init(sc_card_t *card)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
@ -92,6 +131,22 @@ static int etoken_init(sc_card_t *card)
|
|||
_sc_card_add_rsa_alg(card, 768, flags, 0);
|
||||
_sc_card_add_rsa_alg(card, 1024, flags, 0);
|
||||
|
||||
if (card->type == SC_CARD_TYPE_CARDOS_M4_2) {
|
||||
int r = cardos_have_2048bit_package(card);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 1)
|
||||
card->caps |= SC_CARD_CAP_RSA_2048;
|
||||
card->caps |= SC_CARD_CAP_APDU_EXT;
|
||||
}
|
||||
|
||||
if (card->caps & SC_CARD_CAP_RSA_2048) {
|
||||
_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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -174,91 +229,16 @@ static int etoken_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2)
|
|||
return SC_ERROR_CARD_CMD_FAILED;
|
||||
}
|
||||
|
||||
static u8 etoken_extract_offset(u8 *buf, int buflen) {
|
||||
int i;
|
||||
int mode;
|
||||
u8 tag,len;
|
||||
|
||||
tag=0; len=0;
|
||||
mode = 0;
|
||||
|
||||
for (i=0; i < buflen;) {
|
||||
if (mode == 0) {
|
||||
tag = buf[i++];
|
||||
mode=1;
|
||||
continue;
|
||||
}
|
||||
if (mode == 1) {
|
||||
len=buf[i++];
|
||||
mode=2;
|
||||
continue;
|
||||
}
|
||||
if (len == 0) {
|
||||
mode=0;
|
||||
continue;
|
||||
}
|
||||
if (tag == 0x8a && len == 1) {
|
||||
return buf[i];
|
||||
}
|
||||
|
||||
i+=len;
|
||||
mode=0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8* etoken_extract_fid(u8 *buf, int buflen) {
|
||||
int i;
|
||||
int mode;
|
||||
u8 tag,len;
|
||||
|
||||
mode = 0;
|
||||
tag = 0;
|
||||
len = 0;
|
||||
|
||||
|
||||
for (i=0; i < buflen;) {
|
||||
if (mode == 0) {
|
||||
tag = buf[i++];
|
||||
mode=1;
|
||||
continue;
|
||||
}
|
||||
if (mode == 1) {
|
||||
len=buf[i++];
|
||||
mode=2;
|
||||
continue;
|
||||
}
|
||||
if (len == 0) {
|
||||
mode=0;
|
||||
continue;
|
||||
}
|
||||
if ((tag == 0x86) && (len == 2) && (i+1 < buflen)) {
|
||||
return &buf[i];
|
||||
}
|
||||
|
||||
i+=len;
|
||||
mode=0;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int etoken_list_files(sc_card_t *card, u8 *buf, size_t buflen)
|
||||
{
|
||||
sc_apdu_t apdu;
|
||||
u8 rbuf[256];
|
||||
int r;
|
||||
int len;
|
||||
size_t i, fids;
|
||||
u8 offset;
|
||||
u8 *fid;
|
||||
u8 rbuf[256], offset = 0;
|
||||
const u8 *p = rbuf, *q;
|
||||
int r;
|
||||
size_t fids = 0, len;
|
||||
|
||||
SC_FUNC_CALLED(card->ctx, 1);
|
||||
|
||||
fids=0;
|
||||
offset=0;
|
||||
|
||||
/* 0x16: DIRECTORY */
|
||||
/* 0x02: list both DF and EF */
|
||||
|
||||
|
@ -278,34 +258,41 @@ get_next_part:
|
|||
sc_error(card->ctx, "directory listing > 256 bytes, cutting");
|
||||
r = 256;
|
||||
}
|
||||
for (i=0; i < apdu.resplen;) {
|
||||
|
||||
len = apdu.resplen;
|
||||
while (len != 0) {
|
||||
size_t tlen = 0, ilen = 0;
|
||||
/* is there a file informatin block (0x6f) ? */
|
||||
if (rbuf[i] != 0x6f) {
|
||||
sc_error(card->ctx, "directory listing not parseable");
|
||||
p = sc_asn1_find_tag(card->ctx, p, len, 0x6f, &tlen);
|
||||
if (p == NULL) {
|
||||
sc_error(card->ctx, "directory tag missing");
|
||||
return SC_ERROR_INTERNAL;
|
||||
}
|
||||
if (tlen == 0)
|
||||
/* empty directory */
|
||||
break;
|
||||
q = sc_asn1_find_tag(card->ctx, p, tlen, 0x86, &ilen);
|
||||
if (q == NULL || ilen != 2) {
|
||||
sc_error(card->ctx, "error parsing file id TLV object");
|
||||
return SC_ERROR_INTERNAL;
|
||||
}
|
||||
if (i+1 > apdu.resplen) {
|
||||
sc_error(card->ctx, "directory listing short");
|
||||
/* put file id in buf */
|
||||
if (buflen >= 2) {
|
||||
buf[fids++] = q[0];
|
||||
buf[fids++] = q[1];
|
||||
buflen -= 2;
|
||||
} else
|
||||
/* not enought space left in buffer => break */
|
||||
break;
|
||||
/* extract next offset */
|
||||
q = sc_asn1_find_tag(card->ctx, p, tlen, 0x8a, &ilen);
|
||||
if (q != NULL && ilen == 1) {
|
||||
offset = (u8)ilen;
|
||||
if (offset != 0)
|
||||
goto get_next_part;
|
||||
}
|
||||
len = rbuf[i+1];
|
||||
if (i + 1 + len > apdu.resplen) {
|
||||
sc_error(card->ctx, "directory listing short");
|
||||
break;
|
||||
}
|
||||
fid = etoken_extract_fid(&rbuf[i+2], len);
|
||||
|
||||
if (fid) {
|
||||
if (fids + 2 >= buflen)
|
||||
break;
|
||||
buf[fids++] = fid[0];
|
||||
buf[fids++] = fid[1];
|
||||
}
|
||||
|
||||
offset = etoken_extract_offset(&rbuf[i+2], len);
|
||||
if (offset)
|
||||
goto get_next_part;
|
||||
i += len+2;
|
||||
len -= tlen + 2;
|
||||
p += tlen;
|
||||
}
|
||||
|
||||
r = fids;
|
||||
|
@ -377,7 +364,7 @@ static const int ef_acl[9] = {
|
|||
|
||||
SC_AC_OP_INVALIDATE, /* EF */
|
||||
SC_AC_OP_REHABILITATE, /* EF */
|
||||
SC_AC_OP_ERASE, /* (delete) EF */
|
||||
SC_AC_OP_DELETE, /* (delete) EF */
|
||||
|
||||
/* XXX: ADMIN should be an ACL type of its own, or mapped
|
||||
* to erase */
|
||||
|
@ -412,47 +399,56 @@ static int etoken_select_file(sc_card_t *card,
|
|||
SC_FUNC_RETURN(card->ctx, 1, r);
|
||||
}
|
||||
|
||||
static int etoken_create_file(sc_card_t *card, sc_file_t *file)
|
||||
static int cardos_acl_to_bytes(sc_card_t *card, const sc_file_t *file,
|
||||
u8 *buf, size_t *outlen)
|
||||
{
|
||||
int r, i, byte;
|
||||
int i, byte;
|
||||
const int *idx;
|
||||
u8 acl[9], type[3], status[3];
|
||||
|
||||
if (card->ctx->debug >= 1) {
|
||||
char pbuf[128+1];
|
||||
size_t n;
|
||||
if (buf == NULL || *outlen < 9)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
|
||||
for (n = 0; n < file->path.len; n++) {
|
||||
snprintf(pbuf + 2 * n, sizeof(pbuf) - 2 * n,
|
||||
"%02X", file->path.value[n]);
|
||||
idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl;
|
||||
for (i = 0; i < 9; i++) {
|
||||
if (idx[i] < 0)
|
||||
byte = 0x00;
|
||||
else
|
||||
byte = acl_to_byte(sc_file_get_acl_entry(file, idx[i]));
|
||||
if (byte < 0) {
|
||||
sc_error(card->ctx, "Invalid ACL\n");
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
|
||||
sc_debug(card->ctx, "etoken_create_file(%s)\n", pbuf);
|
||||
buf[i] = byte;
|
||||
}
|
||||
*outlen = 9;
|
||||
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
static int cardos_set_file_attributes(sc_card_t *card, sc_file_t *file)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (file->type_attr_len == 0) {
|
||||
u8 type[3];
|
||||
|
||||
memset(type, 0, sizeof(type));
|
||||
type[0] = 0x00;
|
||||
switch (file->type) {
|
||||
case SC_FILE_TYPE_WORKING_EF:
|
||||
break;
|
||||
case SC_FILE_TYPE_INTERNAL_EF:
|
||||
type[0] = 0x08;
|
||||
break;
|
||||
case SC_FILE_TYPE_DF:
|
||||
type[0] = 0x38;
|
||||
break;
|
||||
default:
|
||||
r = SC_ERROR_NOT_SUPPORTED;
|
||||
goto out;
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
if (file->type != SC_FILE_TYPE_DF) {
|
||||
switch (file->ef_structure) {
|
||||
case SC_FILE_EF_LINEAR_FIXED_TLV:
|
||||
case SC_FILE_EF_LINEAR_VARIABLE:
|
||||
case SC_FILE_EF_CYCLIC_TLV:
|
||||
r = SC_ERROR_NOT_SUPPORTED;
|
||||
goto out;
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
/* No idea what this means, but it
|
||||
* seems to be required for key
|
||||
* generation. */
|
||||
|
@ -464,10 +460,12 @@ static int etoken_create_file(sc_card_t *card, sc_file_t *file)
|
|||
}
|
||||
}
|
||||
r = sc_file_set_type_attr(file, type, sizeof(type));
|
||||
if (r)
|
||||
goto out;
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
}
|
||||
if (file->prop_attr_len == 0) {
|
||||
u8 status[3];
|
||||
|
||||
status[0] = 0x01;
|
||||
if (file->type == SC_FILE_TYPE_DF) {
|
||||
status[1] = file->size >> 8;
|
||||
|
@ -476,34 +474,164 @@ static int etoken_create_file(sc_card_t *card, sc_file_t *file)
|
|||
status[1] = status[2] = 0x00; /* not used */
|
||||
}
|
||||
r = sc_file_set_prop_attr(file, status, sizeof(status));
|
||||
if (r)
|
||||
goto out;
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
}
|
||||
if (file->sec_attr_len == 0) {
|
||||
idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl;
|
||||
for (i = 0; i < 9; i++) {
|
||||
if (idx[i] < 0)
|
||||
byte = 0x00;
|
||||
else
|
||||
byte = acl_to_byte(
|
||||
sc_file_get_acl_entry(file, idx[i]));
|
||||
if (byte < 0) {
|
||||
sc_error(card->ctx, "Invalid ACL\n");
|
||||
r = SC_ERROR_INVALID_ARGUMENTS;
|
||||
goto out;
|
||||
}
|
||||
acl[i] = byte;
|
||||
}
|
||||
r = sc_file_set_sec_attr(file, acl, sizeof(acl));
|
||||
if (r)
|
||||
goto out;
|
||||
u8 acl[9];
|
||||
size_t blen = sizeof(acl);
|
||||
|
||||
r = cardos_acl_to_bytes(card, file, acl, &blen);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
r = sc_file_set_sec_attr(file, acl, blen);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
}
|
||||
r = iso_ops->create_file(card, file);
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
/* FIXME: if this is a DF and there's an AID, set it here
|
||||
* using PUT_DATA_FCI */
|
||||
/* newer versions of cardos seems to prefer the FCP */
|
||||
static int cardos_construct_fcp(sc_card_t *card, const sc_file_t *file,
|
||||
u8 *out, size_t *outlen)
|
||||
{
|
||||
u8 buf[64], *p = out;
|
||||
size_t inlen = *outlen, len;
|
||||
int r;
|
||||
|
||||
out: SC_FUNC_RETURN(card->ctx, 1, r);
|
||||
SC_FUNC_CALLED(card->ctx, 2);
|
||||
|
||||
if (out == NULL || inlen < 64)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
/* add FCP tag */
|
||||
*p++ = 0x62;
|
||||
/* we will add the length later */
|
||||
p++;
|
||||
|
||||
/* set the length */
|
||||
buf[0] = (file->size >> 8) & 0xff;
|
||||
buf[1] = file->size & 0xff;
|
||||
if (file->type == SC_FILE_TYPE_DF)
|
||||
r = sc_asn1_put_tag(0x81, buf, 2, p, 4, &p);
|
||||
else
|
||||
r = sc_asn1_put_tag(0x80, buf, 2, p, 4, &p);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
/* set file type */
|
||||
if (file->shareable != 0)
|
||||
buf[0] = 0x40;
|
||||
else
|
||||
buf[0] = 0x00;
|
||||
if (file->type == SC_FILE_TYPE_WORKING_EF) {
|
||||
switch (file->ef_structure) {
|
||||
case SC_FILE_EF_TRANSPARENT:
|
||||
buf[0] |= 0x01;
|
||||
break;
|
||||
case SC_FILE_EF_LINEAR_VARIABLE_TLV:
|
||||
buf[0] |= 0x05;
|
||||
break;
|
||||
case SC_FILE_EF_LINEAR_FIXED:
|
||||
buf[0] |= 0x02;
|
||||
buf[1] |= 0x21;
|
||||
buf[2] |= 0x00;
|
||||
buf[3] |= (u8) file->record_length;
|
||||
buf[4] |= (u8) file->record_count;
|
||||
break;
|
||||
case SC_FILE_EF_CYCLIC:
|
||||
buf[0] |= 0x06;
|
||||
buf[1] |= 0x21;
|
||||
buf[2] |= 0x00;
|
||||
buf[3] |= (u8) file->record_length;
|
||||
buf[4] |= (u8) file->record_count;
|
||||
break;
|
||||
default:
|
||||
sc_error(card->ctx, "unknown EF type: %u", file->type);
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
if (file->ef_structure == SC_FILE_EF_CYCLIC ||
|
||||
file->ef_structure == SC_FILE_EF_LINEAR_FIXED)
|
||||
r = sc_asn1_put_tag(0x82, buf, 5, p, 8, &p);
|
||||
else
|
||||
r = sc_asn1_put_tag(0x82, buf, 1, p, 8, &p);
|
||||
} else if (file->type == SC_FILE_TYPE_DF) {
|
||||
buf[0] |= 0x38;
|
||||
r = sc_asn1_put_tag(0x82, buf, 1, p, 8, &p);
|
||||
} else
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
/* set file id */
|
||||
buf[0] = (file->id >> 8) & 0xff;
|
||||
buf[1] = file->id & 0xff;
|
||||
r = sc_asn1_put_tag(0x83, buf, 2, p, 8, &p);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
/* set aid (for DF only) */
|
||||
if (file->type == SC_FILE_TYPE_DF && file->namelen != 0) {
|
||||
r = sc_asn1_put_tag(0x84, file->name, file->namelen, p, 20, &p);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
}
|
||||
/* set proprietary file attributes */
|
||||
buf[0] = 0x00; /* use default values */
|
||||
if (file->type == SC_FILE_TYPE_DF)
|
||||
r = sc_asn1_put_tag(0x85, buf, 1, p, 8, &p);
|
||||
else {
|
||||
buf[1] = 0x00;
|
||||
buf[2] = 0x00;
|
||||
r = sc_asn1_put_tag(0x85, buf, 1, p, 8, &p);
|
||||
}
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
/* set ACs */
|
||||
len = 9;
|
||||
r = cardos_acl_to_bytes(card, file, buf, &len);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
r = sc_asn1_put_tag(0x86, buf, len, p, 18, &p);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
/* finally set the length of the FCP */
|
||||
out[1] = p - out - 2;
|
||||
|
||||
*outlen = p - out;
|
||||
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
static int cardos_create_file(sc_card_t *card, sc_file_t *file)
|
||||
{
|
||||
int r;
|
||||
|
||||
SC_FUNC_CALLED(card->ctx, 1);
|
||||
|
||||
if (card->type == SC_CARD_TYPE_ETOKEN_GENERIC) {
|
||||
r = cardos_set_file_attributes(card, file);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
return iso_ops->create_file(card, file);
|
||||
} else if (card->type == SC_CARD_TYPE_CARDOS_M4_2) {
|
||||
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
size_t len = sizeof(sbuf);
|
||||
sc_apdu_t apdu;
|
||||
|
||||
r = cardos_construct_fcp(card, file, sbuf, &len);
|
||||
if (r < 0) {
|
||||
sc_error(card->ctx, "unable to create FCP");
|
||||
return r;
|
||||
}
|
||||
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00);
|
||||
apdu.lc = len;
|
||||
apdu.datalen = len;
|
||||
apdu.data = sbuf;
|
||||
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
|
||||
return sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
} else
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -594,34 +722,26 @@ do_compute_signature(sc_card_t *card, const u8 *data, size_t datalen,
|
|||
{
|
||||
int r;
|
||||
sc_apdu_t apdu;
|
||||
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
|
||||
|
||||
if (datalen > SC_MAX_APDU_BUFFER_SIZE ||
|
||||
outlen > SC_MAX_APDU_BUFFER_SIZE)
|
||||
return SC_ERROR_INTERNAL;
|
||||
|
||||
/* INS: 0x2A PERFORM SECURITY OPERATION
|
||||
* P1: 0x9E Resp: Digital Signature
|
||||
* P2: 0x9A Cmd: Input for Digital Signature */
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A);
|
||||
apdu.resp = rbuf;
|
||||
apdu.le = outlen;
|
||||
apdu.resplen = sizeof(rbuf);
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x9E, 0x9A);
|
||||
apdu.resp = out;
|
||||
apdu.le = outlen;
|
||||
apdu.resplen = outlen;
|
||||
|
||||
memcpy(sbuf, data, datalen);
|
||||
apdu.data = sbuf;
|
||||
apdu.lc = datalen;
|
||||
apdu.data = data;
|
||||
apdu.lc = datalen;
|
||||
apdu.datalen = datalen;
|
||||
apdu.sensitive = 1;
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
|
||||
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
|
||||
memcpy(out, rbuf, outlen);
|
||||
SC_FUNC_RETURN(card->ctx, 4, apdu.resplen);
|
||||
}
|
||||
SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2));
|
||||
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
|
||||
SC_FUNC_RETURN(card->ctx, 4, apdu.resplen)
|
||||
else
|
||||
SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2))
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -637,7 +757,7 @@ etoken_compute_signature(sc_card_t *card, const u8 *data, size_t datalen,
|
|||
ctx = card->ctx;
|
||||
SC_FUNC_CALLED(ctx, 1);
|
||||
|
||||
if (datalen > 255)
|
||||
if (datalen > SC_MAX_APDU_BUFFER_SIZE)
|
||||
SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
|
||||
if (outlen < datalen)
|
||||
SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_BUFFER_TOO_SMALL);
|
||||
|
@ -688,7 +808,7 @@ etoken_lifecycle_get(sc_card_t *card, int *mode)
|
|||
|
||||
SC_FUNC_CALLED(card->ctx, 1);
|
||||
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 01, 0x83);
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x83);
|
||||
apdu.cla = 0x00;
|
||||
apdu.le = 256;
|
||||
apdu.resplen = sizeof(rbuf);
|
||||
|
@ -835,7 +955,6 @@ etoken_generate_key(sc_card_t *card,
|
|||
apdu.cla = 0x00;
|
||||
apdu.ins = 0x46;
|
||||
apdu.p1 = 0x00;
|
||||
apdu.p2 = args->key_id;/* doc is not clear, it just says "ID" */
|
||||
apdu.p2 = 0x00;
|
||||
apdu.data= data;
|
||||
apdu.datalen = apdu.lc = sizeof(data);
|
||||
|
@ -848,7 +967,7 @@ etoken_generate_key(sc_card_t *card,
|
|||
return r;
|
||||
}
|
||||
|
||||
static int etoken_get_serialnr(sc_card_t *card, sc_serial_number_t *serial)
|
||||
static int cardos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial)
|
||||
{
|
||||
int r;
|
||||
sc_apdu_t apdu;
|
||||
|
@ -897,7 +1016,7 @@ etoken_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
|
|||
case SC_CARDCTL_LIFECYCLE_SET:
|
||||
return etoken_lifecycle_set(card, (int *) ptr);
|
||||
case SC_CARDCTL_GET_SERIALNR:
|
||||
return etoken_get_serialnr(card, (sc_serial_number_t *)ptr);
|
||||
return cardos_get_serialnr(card, (sc_serial_number_t *)ptr);
|
||||
}
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
@ -922,6 +1041,30 @@ etoken_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data,
|
|||
return iso_ops->pin_cmd(card, data, tries_left);
|
||||
}
|
||||
|
||||
static int cardos_logout(sc_card_t *card)
|
||||
{
|
||||
if (card->type == SC_CARD_TYPE_CARDOS_M4_2) {
|
||||
sc_apdu_t apdu;
|
||||
int r;
|
||||
sc_path_t path;
|
||||
|
||||
sc_format_path("3F00", &path);
|
||||
r = sc_select_file(card, &path, NULL);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
|
||||
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xEA, 0x00, 0x00);
|
||||
apdu.cla = 0x80;
|
||||
|
||||
r = sc_transmit_apdu(card, &apdu);
|
||||
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||
|
||||
return sc_check_sw(card, apdu.sw1, apdu.sw2);
|
||||
} else
|
||||
return iso_ops->logout(card);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* eToken R2 supports WRITE_BINARY, PRO Tokens support UPDATE_BINARY */
|
||||
|
||||
|
@ -934,7 +1077,7 @@ static struct sc_card_driver * sc_get_driver(void)
|
|||
etoken_ops.init = etoken_init;
|
||||
etoken_ops.finish = etoken_finish;
|
||||
etoken_ops.select_file = etoken_select_file;
|
||||
etoken_ops.create_file = etoken_create_file;
|
||||
etoken_ops.create_file = cardos_create_file;
|
||||
etoken_ops.set_security_env = etoken_set_security_env;
|
||||
etoken_ops.restore_security_env = etoken_restore_security_env;
|
||||
etoken_ops.compute_signature = etoken_compute_signature;
|
||||
|
@ -943,6 +1086,7 @@ static struct sc_card_driver * sc_get_driver(void)
|
|||
etoken_ops.check_sw = etoken_check_sw;
|
||||
etoken_ops.card_ctl = etoken_card_ctl;
|
||||
etoken_ops.pin_cmd = etoken_pin_cmd;
|
||||
etoken_ops.logout = cardos_logout;
|
||||
|
||||
return &etoken_drv;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ enum {
|
|||
/* etoken driver */
|
||||
SC_CARD_TYPE_ETOKEN_BASE = 1000,
|
||||
SC_CARD_TYPE_ETOKEN_GENERIC,
|
||||
SC_CARD_TYPE_CARDOS_M4_2,
|
||||
|
||||
/* flex/cyberflex drivers */
|
||||
SC_CARD_TYPE_FLEX_BASE = 2000,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* CardOS specific operation for PKCS15 initialization
|
||||
*
|
||||
* Copyright (C) 2005 Nils Larsch <nils@larsch.net>
|
||||
* Copyright (C) 2002 Olaf Kirch <okir@lst.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -29,6 +30,8 @@
|
|||
#include <opensc/opensc.h>
|
||||
#include <opensc/cardctl.h>
|
||||
#include <opensc/log.h>
|
||||
#include <opensc/cards.h>
|
||||
#include <opensc/asn1.h>
|
||||
#include "pkcs15-init.h"
|
||||
#include "profile.h"
|
||||
|
||||
|
@ -42,14 +45,6 @@ struct tlv {
|
|||
unsigned char * current;
|
||||
unsigned char * next;
|
||||
};
|
||||
#define RSAKEY_MAX_BITS 1024
|
||||
#define RSAKEY_MAX_SIZE (RSAKEY_MAX_BITS/8)
|
||||
struct rsakey {
|
||||
struct bignum {
|
||||
size_t len;
|
||||
u8 data[RSAKEY_MAX_SIZE];
|
||||
} n, d;
|
||||
};
|
||||
|
||||
/*
|
||||
* Local functions
|
||||
|
@ -59,12 +54,14 @@ static int etoken_store_pin(sc_profile_t *profile, sc_card_t *card,
|
|||
const u8 *pin, size_t pin_len);
|
||||
static int etoken_create_sec_env(sc_profile_t *, sc_card_t *,
|
||||
unsigned int, unsigned int);
|
||||
static int etoken_put_key(struct sc_profile *, sc_card_t *,
|
||||
static int cardos_put_key(struct sc_profile *, sc_card_t *,
|
||||
int, sc_pkcs15_prkey_info_t *,
|
||||
struct sc_pkcs15_prkey_rsa *);
|
||||
static int etoken_key_algorithm(unsigned int, int *);
|
||||
static int etoken_extract_pubkey(sc_card_t *, int,
|
||||
u8, sc_pkcs15_bignum_t *);
|
||||
static int cardos_key_algorithm(unsigned int, size_t, int *);
|
||||
static int cardos_extract_pubkey(sc_card_t *, sc_pkcs15_pubkey_t *,
|
||||
sc_file_t *, int);
|
||||
static int etoken_extract_pubkey(sc_card_t *card, int nr, u8 tag,
|
||||
sc_pkcs15_bignum_t *bn);
|
||||
|
||||
/* Object IDs for PIN objects.
|
||||
* SO PIN = 0x01, SO PUK = 0x02
|
||||
|
@ -82,8 +79,8 @@ static int etoken_extract_pubkey(sc_card_t *, int,
|
|||
#define ETOKEN_ALGO_RSA_PURE_SIG 0x8C
|
||||
#define ETOKEN_ALGO_RSA_SIG_SHA1 0xC8
|
||||
#define ETOKEN_ALGO_RSA_PURE_SIG_SHA1 0xCC
|
||||
#define ETOKEN_SIGN_RSA ETOKEN_ALGO_RSA_PURE_SIG
|
||||
#define ETOKEN_DECIPHER_RSA ETOKEN_ALGO_RSA_PURE
|
||||
#define CARDOS_ALGO_EXT_RSA_PURE 0x0a
|
||||
#define CARDOS_ALGO_EXT_RSA_SIG_PURE 0x8a
|
||||
#define ETOKEN_ALGO_PIN 0x87
|
||||
|
||||
static inline void
|
||||
|
@ -215,8 +212,7 @@ etoken_create_pin(sc_profile_t *profile, sc_card_t *card, sc_file_t *df,
|
|||
|
||||
if (r >= 0) {
|
||||
r = etoken_store_pin(profile, card,
|
||||
pin_info, puk_id,
|
||||
pin, pin_len);
|
||||
pin_info, puk_id, pin, pin_len);
|
||||
}
|
||||
|
||||
return r;
|
||||
|
@ -267,17 +263,39 @@ etoken_store_key(sc_profile_t *profile, sc_card_t *card,
|
|||
return SC_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (etoken_key_algorithm(key_info->usage, &algorithm) < 0) {
|
||||
if (cardos_key_algorithm(key_info->usage, key_info->modulus_length, &algorithm) < 0) {
|
||||
sc_error(card->ctx, "CardOS does not support keys "
|
||||
"that can both sign _and_ decrypt.");
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
r = etoken_put_key(profile, card, algorithm, key_info, &key->u.rsa);
|
||||
r = cardos_put_key(profile, card, algorithm, key_info, &key->u.rsa);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void init_key_object(struct sc_pkcs15_prkey_rsa *key,
|
||||
u8 *data, size_t len)
|
||||
{
|
||||
/* Create a key object, initializing components to 0xff */
|
||||
memset(key, 0x00, sizeof(*key));
|
||||
memset(data, 0xff, len);
|
||||
key->modulus.data = data;
|
||||
key->modulus.len = len;
|
||||
key->d.data = data;
|
||||
key->d.len = len;
|
||||
key->p.len = len >> 1;
|
||||
key->p.data = data;
|
||||
key->q.len = len >> 1;
|
||||
key->q.data = data;
|
||||
key->iqmp.len = len >> 1;
|
||||
key->iqmp.data = data;
|
||||
key->dmp1.len = len >> 1;
|
||||
key->dmp1.data = data;
|
||||
key->dmq1.len = len >> 1;
|
||||
key->dmq1.data = data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Key generation
|
||||
*/
|
||||
|
@ -290,47 +308,46 @@ etoken_generate_key(sc_profile_t *profile, sc_card_t *card,
|
|||
struct sc_pkcs15_prkey_rsa key_obj;
|
||||
struct sc_cardctl_etoken_genkey_info args;
|
||||
struct sc_file *temp;
|
||||
u8 abignum[RSAKEY_MAX_SIZE];
|
||||
unsigned int keybits;
|
||||
int algorithm, r, delete_it = 0;
|
||||
u8 abignum[256];
|
||||
int algorithm, r, delete_it = 0, use_ext_rsa = 0;
|
||||
size_t keybits, rsa_max_size;
|
||||
|
||||
if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) {
|
||||
sc_error(card->ctx, "CardOS supports only RSA keys.");
|
||||
if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA)
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
|
||||
rsa_max_size = (card->caps & SC_CARD_CAP_RSA_2048) ? 2048 : 1024;
|
||||
keybits = key_info->modulus_length & ~7UL;
|
||||
if (keybits > rsa_max_size) {
|
||||
sc_error(card->ctx, "Unable to generate key, max size is %lu", rsa_max_size);
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
|
||||
if (etoken_key_algorithm(key_info->usage, &algorithm) < 0) {
|
||||
if (keybits > 1024)
|
||||
use_ext_rsa = 1;
|
||||
|
||||
if (cardos_key_algorithm(key_info->usage, keybits, &algorithm) < 0) {
|
||||
sc_error(card->ctx, "CardOS does not support keys "
|
||||
"that can both sign _and_ decrypt.");
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
keybits = key_info->modulus_length & ~7UL;
|
||||
if (keybits > RSAKEY_MAX_BITS) {
|
||||
sc_error(card->ctx, "Unable to generate key, max size is %d",
|
||||
RSAKEY_MAX_BITS);
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
|
||||
if (sc_profile_get_file(profile, "tempfile", &temp) < 0) {
|
||||
sc_error(card->ctx, "Profile doesn't define temporary file "
|
||||
"for key generation.");
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
if (use_ext_rsa == 0)
|
||||
temp->ef_structure = SC_FILE_EF_LINEAR_VARIABLE_TLV;
|
||||
else
|
||||
temp->ef_structure = SC_FILE_EF_TRANSPARENT;
|
||||
|
||||
if ((r = sc_pkcs15init_create_file(profile, card, temp)) < 0)
|
||||
goto out;
|
||||
delete_it = 1;
|
||||
|
||||
/* Create a key object, initializing components to 0xff */
|
||||
memset(&key_obj, 0, sizeof(key_obj));
|
||||
memset(abignum, 0xFF, sizeof(abignum));
|
||||
key_obj.modulus.data = abignum;
|
||||
key_obj.modulus.len = keybits >> 3;
|
||||
key_obj.d.data = abignum;
|
||||
key_obj.d.len = keybits >> 3;
|
||||
r = etoken_put_key(profile, card, algorithm, key_info, &key_obj);
|
||||
init_key_object(&key_obj, abignum, keybits >> 3);
|
||||
|
||||
r = cardos_put_key(profile, card, algorithm, key_info, &key_obj);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
|
@ -342,21 +359,12 @@ etoken_generate_key(sc_profile_t *profile, sc_card_t *card,
|
|||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
/* extract public key from file and delete it */
|
||||
if ((r = sc_select_file(card, &temp->path, NULL)) < 0)
|
||||
goto out;
|
||||
r = etoken_extract_pubkey(card, 1, 0x10, &pubkey->u.rsa.modulus);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
r = etoken_extract_pubkey(card, 2, 0x11, &pubkey->u.rsa.exponent);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
pubkey->algorithm = SC_ALGORITHM_RSA;
|
||||
|
||||
out: if (delete_it) {
|
||||
r = cardos_extract_pubkey(card, pubkey, temp, use_ext_rsa);
|
||||
out:
|
||||
if (delete_it != 0)
|
||||
sc_pkcs15init_rmdir(card, profile, temp);
|
||||
}
|
||||
sc_file_free(temp);
|
||||
|
||||
if (r < 0) {
|
||||
if (pubkey->u.rsa.modulus.data)
|
||||
free (pubkey->u.rsa.modulus.data);
|
||||
|
@ -379,13 +387,17 @@ etoken_store_pin(sc_profile_t *profile, sc_card_t *card,
|
|||
unsigned char pinpadded[16];
|
||||
struct tlv tlv;
|
||||
unsigned int attempts, minlen, maxlen;
|
||||
int r;
|
||||
|
||||
/* We need to do padding because pkcs15-lib.c does it.
|
||||
* Would be nice to have a flag in the profile that says
|
||||
* "no padding required". */
|
||||
maxlen = MIN(profile->pin_maxlen, sizeof(pinpadded));
|
||||
if (pin_len > maxlen)
|
||||
pin_len = maxlen;
|
||||
if (pin_len > maxlen) {
|
||||
sc_error(card->ctx, "invalid pin length: %u (max %u)\n",
|
||||
pin_len, maxlen);
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
memcpy(pinpadded, pin, pin_len);
|
||||
while (pin_len < maxlen)
|
||||
pinpadded[pin_len++] = profile->pin_pad_char;
|
||||
|
@ -416,9 +428,12 @@ etoken_store_pin(sc_profile_t *profile, sc_card_t *card,
|
|||
tlv_add(&tlv, 0xff);
|
||||
|
||||
/* DEK: not documented, no idea what it means */
|
||||
tlv_add(&tlv, 0x00);
|
||||
tlv_add(&tlv, 0xff);
|
||||
|
||||
/* ARA counter: Nils says this is the userConsent field */
|
||||
/* ARA counter: number of times the test object can be used before
|
||||
* another verification is required (~ user consent)
|
||||
* (0x00 unlimited usage)
|
||||
*/
|
||||
tlv_add(&tlv, 0x00);
|
||||
|
||||
tlv_add(&tlv, minlen); /* minlen */
|
||||
|
@ -437,6 +452,11 @@ etoken_store_pin(sc_profile_t *profile, sc_card_t *card,
|
|||
args.data = buffer;
|
||||
args.len = tlv_len(&tlv);
|
||||
|
||||
/* ensure we are in the correct lifecycle */
|
||||
r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN);
|
||||
if (r < 0 && r != SC_ERROR_NOT_SUPPORTED)
|
||||
return r;
|
||||
|
||||
return sc_card_ctl(card, SC_CARDCTL_ETOKEN_PUT_DATA_OCI, &args);
|
||||
}
|
||||
|
||||
|
@ -450,6 +470,7 @@ etoken_create_sec_env(struct sc_profile *profile, sc_card_t *card,
|
|||
struct sc_cardctl_etoken_obj_info args;
|
||||
struct tlv tlv;
|
||||
unsigned char buffer[64];
|
||||
int r;
|
||||
|
||||
tlv_init(&tlv, buffer, sizeof(buffer));
|
||||
tlv_next(&tlv, 0x83);
|
||||
|
@ -469,6 +490,12 @@ etoken_create_sec_env(struct sc_profile *profile, sc_card_t *card,
|
|||
|
||||
args.data = buffer;
|
||||
args.len = tlv_len(&tlv);
|
||||
|
||||
/* ensure we are in the correct lifecycle */
|
||||
r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN);
|
||||
if (r < 0 && r != SC_ERROR_NOT_SUPPORTED)
|
||||
return r;
|
||||
|
||||
return sc_card_ctl(card, SC_CARDCTL_ETOKEN_PUT_DATA_SECI, &args);
|
||||
}
|
||||
|
||||
|
@ -482,17 +509,22 @@ etoken_create_sec_env(struct sc_profile *profile, sc_card_t *card,
|
|||
#define USAGE_ANY_DECIPHER (SC_PKCS15_PRKEY_USAGE_DECRYPT|\
|
||||
SC_PKCS15_PRKEY_USAGE_UNWRAP)
|
||||
|
||||
static int
|
||||
etoken_key_algorithm(unsigned int usage, int *algop)
|
||||
static int cardos_key_algorithm(unsigned int usage, size_t keylen, int *algop)
|
||||
{
|
||||
int sign = 0, decipher = 0;
|
||||
|
||||
if (usage & USAGE_ANY_SIGN) {
|
||||
*algop = ETOKEN_SIGN_RSA;
|
||||
if (keylen <= 1024)
|
||||
*algop = ETOKEN_ALGO_RSA_PURE_SIG;
|
||||
else
|
||||
*algop = CARDOS_ALGO_EXT_RSA_SIG_PURE;
|
||||
sign = 1;
|
||||
}
|
||||
if (usage & USAGE_ANY_DECIPHER) {
|
||||
*algop = ETOKEN_DECIPHER_RSA;
|
||||
if (keylen <= 1024)
|
||||
*algop = ETOKEN_ALGO_RSA_PURE;
|
||||
else
|
||||
*algop = CARDOS_ALGO_EXT_RSA_PURE;
|
||||
decipher = 1;
|
||||
}
|
||||
return (sign == decipher)? -1 : 0;
|
||||
|
@ -509,12 +541,15 @@ etoken_store_key_component(sc_card_t *card,
|
|||
unsigned int key_id, unsigned int pin_id,
|
||||
unsigned int num,
|
||||
const u8 *data, size_t len,
|
||||
int last)
|
||||
int last, int use_prefix)
|
||||
{
|
||||
struct sc_cardctl_etoken_obj_info args;
|
||||
struct tlv tlv;
|
||||
unsigned char buffer[256];
|
||||
#if SET_SM_BYTES
|
||||
unsigned int n;
|
||||
#endif
|
||||
int r;
|
||||
|
||||
/* Initialize the TLV encoder */
|
||||
tlv_init(&tlv, buffer, sizeof(buffer));
|
||||
|
@ -540,35 +575,46 @@ etoken_store_key_component(sc_card_t *card,
|
|||
tlv_add(&tlv, pin_id); /* AC USE */
|
||||
tlv_add(&tlv, pin_id); /* AC CHANGE */
|
||||
tlv_add(&tlv, pin_id); /* UNKNOWN */
|
||||
/* The next 4 AC bytes are sent by the eToken run-time
|
||||
* as well, but aren't documented anywhere.
|
||||
* Key generation won't work without them, however. */
|
||||
tlv_add(&tlv, 0);
|
||||
tlv_add(&tlv, 0);
|
||||
tlv_add(&tlv, 0);
|
||||
tlv_add(&tlv, 0); /* rfu */
|
||||
tlv_add(&tlv, 0); /* rfu */
|
||||
tlv_add(&tlv, 0); /* rfu */
|
||||
#if 0
|
||||
tlv_add(&tlv, pin_id); /* AC GENKEY */
|
||||
#else
|
||||
tlv_add(&tlv, 0);
|
||||
#endif
|
||||
|
||||
#if SET_SM_BYTES
|
||||
/* it shouldn't be necessary to set the default value */
|
||||
/* SM bytes */
|
||||
tlv_next(&tlv, 0x8B);
|
||||
for (n = 0; n < 16; n++)
|
||||
tlv_add(&tlv, 0xFF);
|
||||
#endif
|
||||
|
||||
/* key component */
|
||||
tlv_next(&tlv, 0x8f);
|
||||
tlv_add(&tlv, len+1);
|
||||
tlv_add(&tlv, 0);
|
||||
if (use_prefix != 0) {
|
||||
tlv_add(&tlv, len+1);
|
||||
tlv_add(&tlv, 0);
|
||||
}
|
||||
while (len--)
|
||||
tlv_add(&tlv, *data++);
|
||||
|
||||
args.data = buffer;
|
||||
args.len = tlv_len(&tlv);
|
||||
|
||||
/* ensure we are in the correct lifecycle */
|
||||
r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN);
|
||||
if (r < 0 && r != SC_ERROR_NOT_SUPPORTED)
|
||||
return r;
|
||||
|
||||
return sc_card_ctl(card, SC_CARDCTL_ETOKEN_PUT_DATA_OCI, &args);
|
||||
}
|
||||
|
||||
static int
|
||||
etoken_put_key(sc_profile_t *profile, sc_card_t *card,
|
||||
int algorithm, sc_pkcs15_prkey_info_t *key_info,
|
||||
struct sc_pkcs15_prkey_rsa *key)
|
||||
static int cardos_put_key(sc_profile_t *profile, sc_card_t *card,
|
||||
int algorithm, sc_pkcs15_prkey_info_t *key_info,
|
||||
struct sc_pkcs15_prkey_rsa *key)
|
||||
{
|
||||
int r, key_id, pin_id;
|
||||
|
||||
|
@ -577,12 +623,33 @@ etoken_put_key(sc_profile_t *profile, sc_card_t *card,
|
|||
if (pin_id < 0)
|
||||
pin_id = 0;
|
||||
|
||||
r = etoken_store_key_component(card, algorithm, key_id, pin_id, 0,
|
||||
key->modulus.data, key->modulus.len, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = etoken_store_key_component(card, algorithm, key_id, pin_id, 1,
|
||||
key->d.data, key->d.len, 1);
|
||||
if (key_info->modulus_length > 1024 && card->type == SC_CARD_TYPE_CARDOS_M4_2) {
|
||||
r = etoken_store_key_component(card, algorithm, key_id, pin_id, 0,
|
||||
key->p.data, key->p.len, 0, 0);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
r = etoken_store_key_component(card, algorithm, key_id, pin_id, 1,
|
||||
key->q.data, key->q.len, 0, 0);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
r = etoken_store_key_component(card, algorithm, key_id, pin_id, 2,
|
||||
key->dmp1.data, key->dmp1.len, 0, 0);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
r = etoken_store_key_component(card, algorithm, key_id, pin_id, 3,
|
||||
key->dmq1.data, key->dmq1.len, 0, 0);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
r = etoken_store_key_component(card, algorithm, key_id, pin_id, 4,
|
||||
key->iqmp.data, key->iqmp.len, 1, 0);
|
||||
} else {
|
||||
r = etoken_store_key_component(card, algorithm, key_id, pin_id, 0,
|
||||
key->modulus.data, key->modulus.len, 0, 1);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
r = etoken_store_key_component(card, algorithm, key_id, pin_id, 1,
|
||||
key->d.data, key->d.len, 1, 1);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -591,6 +658,52 @@ etoken_put_key(sc_profile_t *profile, sc_card_t *card,
|
|||
* Extract a key component from the public key file populated by
|
||||
* GENERATE KEY PAIR
|
||||
*/
|
||||
static int parse_ext_pubkey_file(sc_card_t *card, const u8 *data, size_t len,
|
||||
sc_pkcs15_pubkey_t *pubkey)
|
||||
{
|
||||
const u8 *p;
|
||||
size_t ilen = 0, tlen = 0, i;
|
||||
|
||||
if (data == NULL || len < 32)
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
if (*data++ != 0x7f || *data++ != 0x49) {
|
||||
sc_error(card->ctx, "invalid public key data: missing tag");
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
len -= 2;
|
||||
tlen = *data++ & 0x7f;
|
||||
if (tlen > 3) {
|
||||
sc_error(card->ctx, "invalid public key data: invalid tag length");
|
||||
return SC_ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
for (i = 0; i < tlen; i++)
|
||||
ilen += (*data++) << (8 * (tlen - i - 1));
|
||||
|
||||
p = sc_asn1_find_tag(card->ctx, data, ilen, 0x81, &tlen);
|
||||
if (p == NULL) {
|
||||
sc_error(card->ctx, "invalid public key data: missing modulus");
|
||||
return SC_ERROR_INTERNAL;
|
||||
}
|
||||
pubkey->u.rsa.modulus.len = tlen;
|
||||
pubkey->u.rsa.modulus.data = malloc(tlen);
|
||||
if (pubkey->u.rsa.modulus.data == NULL)
|
||||
return SC_ERROR_OUT_OF_MEMORY;
|
||||
memcpy(pubkey->u.rsa.modulus.data, p, tlen);
|
||||
|
||||
p = sc_asn1_find_tag(card->ctx, data, ilen, 0x82, &tlen);
|
||||
if (p == NULL) {
|
||||
sc_error(card->ctx, "invalid public key data: missing exponent");
|
||||
return SC_ERROR_INTERNAL;
|
||||
}
|
||||
pubkey->u.rsa.exponent.len = tlen;
|
||||
pubkey->u.rsa.exponent.data = malloc(tlen);
|
||||
if (pubkey->u.rsa.exponent.data == NULL)
|
||||
return SC_ERROR_OUT_OF_MEMORY;
|
||||
memcpy(pubkey->u.rsa.exponent.data, p, tlen);
|
||||
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
etoken_extract_pubkey(sc_card_t *card, int nr, u8 tag,
|
||||
sc_pkcs15_bignum_t *bn)
|
||||
|
@ -603,12 +716,47 @@ etoken_extract_pubkey(sc_card_t *card, int nr, u8 tag,
|
|||
return r;
|
||||
count = r - 4;
|
||||
if (count <= 0 || buf[0] != tag || buf[1] != count + 2
|
||||
|| buf[2] != count + 1 || buf[3] != 0)
|
||||
|| buf[2] != count + 1 || buf[3] != 0)
|
||||
return SC_ERROR_INTERNAL;
|
||||
bn->len = count;
|
||||
bn->data = (u8 *) malloc(count);
|
||||
if (bn->data == NULL)
|
||||
return SC_ERROR_OUT_OF_MEMORY;
|
||||
memcpy(bn->data, buf + 4, count);
|
||||
return 0;
|
||||
return SC_SUCCESS;
|
||||
}
|
||||
|
||||
static int cardos_extract_pubkey(sc_card_t *card, sc_pkcs15_pubkey_t *pubkey,
|
||||
sc_file_t *tfile, int use_ext_rsa)
|
||||
{
|
||||
int r;
|
||||
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
|
||||
r = sc_select_file(card, &tfile->path, NULL);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
|
||||
if (use_ext_rsa == 0) {
|
||||
r = etoken_extract_pubkey(card, 1, 0x10, &pubkey->u.rsa.modulus);
|
||||
if (r != SC_SUCCESS)
|
||||
return r;
|
||||
r = etoken_extract_pubkey(card, 2, 0x11, &pubkey->u.rsa.exponent);
|
||||
} else {
|
||||
u8 *buf;
|
||||
|
||||
buf = malloc(tfile->size);
|
||||
if (buf == NULL)
|
||||
return SC_ERROR_OUT_OF_MEMORY;
|
||||
r = sc_read_binary(card, 0, buf, tfile->size, 0);
|
||||
if (r > 0)
|
||||
r = parse_ext_pubkey_file(card, buf, (size_t)r, pubkey);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
pubkey->algorithm = SC_ALGORITHM_RSA;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct sc_pkcs15init_operations sc_pkcs15init_etoken_operations = {
|
||||
|
|
Loading…
Reference in New Issue