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:
nils 2005-12-28 19:38:55 +00:00
parent e1d7384e8a
commit f5902e4f0c
3 changed files with 547 additions and 254 deletions

View File

@ -2,6 +2,7 @@
* card-etoken.c: Support for Siemens CardOS based cards and tokens * card-etoken.c: Support for Siemens CardOS based cards and tokens
* (for example Aladdin eToken PRO, Eutron CryptoIdentity IT-SEC) * (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) 2002 Andreas Jellinghaus <aj@dungeon.inka.de>
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi> * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* *
@ -25,6 +26,8 @@
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
#include <opensc/asn1.h>
/* andreas says: hm, my card only works for small payloads */ /* andreas says: hm, my card only works for small payloads */
/* comment by okir: one of the examples in the developer guide /* comment by okir: one of the examples in the developer guide
* also talks about copying data in chunks of 128. * 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 }, { "3b:f2:98:00:ff:c1:10:31:fe:55:c8:03:15", NULL, NULL, SC_CARD_TYPE_ETOKEN_GENERIC, 0, NULL },
/* 4.01a */ /* 4.01a */
{ "3b:f2:98:00:ff:c1:10:31:fe:55:c8:04:12", NULL, NULL, SC_CARD_TYPE_ETOKEN_GENERIC, 0, NULL }, { "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 :/ */ /* M4.2 */
/* { "3b f2 18 00 ff c1 0a 31 fe 55 c8 06 8a", NULL, NULL, SC_CARD_TYPE_ETOKEN_GENERIC, 0, NULL }, */ { "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 */ /* 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 }, { "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 */ /* Italian eID card, infocamere */
@ -75,6 +78,42 @@ static int etoken_match_card(sc_card_t *card)
return 1; 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) static int etoken_init(sc_card_t *card)
{ {
unsigned long flags; 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, 768, flags, 0);
_sc_card_add_rsa_alg(card, 1024, 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; 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; 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) static int etoken_list_files(sc_card_t *card, u8 *buf, size_t buflen)
{ {
sc_apdu_t apdu; sc_apdu_t apdu;
u8 rbuf[256]; u8 rbuf[256], offset = 0;
int r; const u8 *p = rbuf, *q;
int len; int r;
size_t i, fids; size_t fids = 0, len;
u8 offset;
u8 *fid;
SC_FUNC_CALLED(card->ctx, 1); SC_FUNC_CALLED(card->ctx, 1);
fids=0;
offset=0;
/* 0x16: DIRECTORY */ /* 0x16: DIRECTORY */
/* 0x02: list both DF and EF */ /* 0x02: list both DF and EF */
@ -278,34 +258,41 @@ get_next_part:
sc_error(card->ctx, "directory listing > 256 bytes, cutting"); sc_error(card->ctx, "directory listing > 256 bytes, cutting");
r = 256; 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) ? */ /* is there a file informatin block (0x6f) ? */
if (rbuf[i] != 0x6f) { p = sc_asn1_find_tag(card->ctx, p, len, 0x6f, &tlen);
sc_error(card->ctx, "directory listing not parseable"); if (p == NULL) {
sc_error(card->ctx, "directory tag missing");
return SC_ERROR_INTERNAL;
}
if (tlen == 0)
/* empty directory */
break; 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) { /* put file id in buf */
sc_error(card->ctx, "directory listing short"); if (buflen >= 2) {
buf[fids++] = q[0];
buf[fids++] = q[1];
buflen -= 2;
} else
/* not enought space left in buffer => break */
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]; len -= tlen + 2;
if (i + 1 + len > apdu.resplen) { p += tlen;
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;
} }
r = fids; r = fids;
@ -377,7 +364,7 @@ static const int ef_acl[9] = {
SC_AC_OP_INVALIDATE, /* EF */ SC_AC_OP_INVALIDATE, /* EF */
SC_AC_OP_REHABILITATE, /* 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 /* XXX: ADMIN should be an ACL type of its own, or mapped
* to erase */ * to erase */
@ -412,47 +399,56 @@ static int etoken_select_file(sc_card_t *card,
SC_FUNC_RETURN(card->ctx, 1, r); 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; const int *idx;
u8 acl[9], type[3], status[3];
if (card->ctx->debug >= 1) { if (buf == NULL || *outlen < 9)
char pbuf[128+1]; return SC_ERROR_INVALID_ARGUMENTS;
size_t n;
for (n = 0; n < file->path.len; n++) { idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl;
snprintf(pbuf + 2 * n, sizeof(pbuf) - 2 * n, for (i = 0; i < 9; i++) {
"%02X", file->path.value[n]); 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;
} }
buf[i] = byte;
sc_debug(card->ctx, "etoken_create_file(%s)\n", pbuf);
} }
*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) { if (file->type_attr_len == 0) {
u8 type[3];
memset(type, 0, sizeof(type)); memset(type, 0, sizeof(type));
type[0] = 0x00; type[0] = 0x00;
switch (file->type) { switch (file->type) {
case SC_FILE_TYPE_WORKING_EF: case SC_FILE_TYPE_WORKING_EF:
break; break;
case SC_FILE_TYPE_INTERNAL_EF:
type[0] = 0x08;
break;
case SC_FILE_TYPE_DF: case SC_FILE_TYPE_DF:
type[0] = 0x38; type[0] = 0x38;
break; break;
default: default:
r = SC_ERROR_NOT_SUPPORTED; return SC_ERROR_NOT_SUPPORTED;
goto out;
} }
if (file->type != SC_FILE_TYPE_DF) { if (file->type != SC_FILE_TYPE_DF) {
switch (file->ef_structure) { switch (file->ef_structure) {
case SC_FILE_EF_LINEAR_FIXED_TLV: case SC_FILE_EF_LINEAR_FIXED_TLV:
case SC_FILE_EF_LINEAR_VARIABLE: case SC_FILE_EF_LINEAR_VARIABLE:
case SC_FILE_EF_CYCLIC_TLV: case SC_FILE_EF_CYCLIC_TLV:
r = SC_ERROR_NOT_SUPPORTED; return SC_ERROR_NOT_SUPPORTED;
goto out;
/* No idea what this means, but it /* No idea what this means, but it
* seems to be required for key * seems to be required for key
* generation. */ * 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)); r = sc_file_set_type_attr(file, type, sizeof(type));
if (r) if (r != SC_SUCCESS)
goto out; return r;
} }
if (file->prop_attr_len == 0) { if (file->prop_attr_len == 0) {
u8 status[3];
status[0] = 0x01; status[0] = 0x01;
if (file->type == SC_FILE_TYPE_DF) { if (file->type == SC_FILE_TYPE_DF) {
status[1] = file->size >> 8; 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 */ status[1] = status[2] = 0x00; /* not used */
} }
r = sc_file_set_prop_attr(file, status, sizeof(status)); r = sc_file_set_prop_attr(file, status, sizeof(status));
if (r) if (r != SC_SUCCESS)
goto out; return r;
} }
if (file->sec_attr_len == 0) { if (file->sec_attr_len == 0) {
idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl; u8 acl[9];
for (i = 0; i < 9; i++) { size_t blen = sizeof(acl);
if (idx[i] < 0)
byte = 0x00; r = cardos_acl_to_bytes(card, file, acl, &blen);
else if (r != SC_SUCCESS)
byte = acl_to_byte( return r;
sc_file_get_acl_entry(file, idx[i])); r = sc_file_set_sec_attr(file, acl, blen);
if (byte < 0) { if (r != SC_SUCCESS)
sc_error(card->ctx, "Invalid ACL\n"); return r;
r = SC_ERROR_INVALID_ARGUMENTS;
goto out;
}
acl[i] = byte;
}
r = sc_file_set_sec_attr(file, acl, sizeof(acl));
if (r)
goto out;
} }
r = iso_ops->create_file(card, file); return SC_SUCCESS;
}
/* FIXME: if this is a DF and there's an AID, set it here /* newer versions of cardos seems to prefer the FCP */
* using PUT_DATA_FCI */ 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; int r;
sc_apdu_t apdu; 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 /* INS: 0x2A PERFORM SECURITY OPERATION
* P1: 0x9E Resp: Digital Signature * P1: 0x9E Resp: Digital Signature
* P2: 0x9A Cmd: Input for Digital Signature */ * P2: 0x9A Cmd: Input for Digital Signature */
sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x9E, 0x9A);
apdu.resp = rbuf; apdu.resp = out;
apdu.le = outlen; apdu.le = outlen;
apdu.resplen = sizeof(rbuf); apdu.resplen = outlen;
memcpy(sbuf, data, datalen); apdu.data = data;
apdu.data = sbuf; apdu.lc = datalen;
apdu.lc = datalen;
apdu.datalen = datalen; apdu.datalen = datalen;
apdu.sensitive = 1; apdu.sensitive = 1;
r = sc_transmit_apdu(card, &apdu); r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed"); SC_TEST_RET(card->ctx, r, "APDU transmit failed");
if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { 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, apdu.resplen); else
} SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2))
SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2));
} }
static int static int
@ -637,7 +757,7 @@ etoken_compute_signature(sc_card_t *card, const u8 *data, size_t datalen,
ctx = card->ctx; ctx = card->ctx;
SC_FUNC_CALLED(ctx, 1); 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); SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
if (outlen < datalen) if (outlen < datalen)
SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_BUFFER_TOO_SMALL); 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_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.cla = 0x00;
apdu.le = 256; apdu.le = 256;
apdu.resplen = sizeof(rbuf); apdu.resplen = sizeof(rbuf);
@ -835,7 +955,6 @@ etoken_generate_key(sc_card_t *card,
apdu.cla = 0x00; apdu.cla = 0x00;
apdu.ins = 0x46; apdu.ins = 0x46;
apdu.p1 = 0x00; apdu.p1 = 0x00;
apdu.p2 = args->key_id;/* doc is not clear, it just says "ID" */
apdu.p2 = 0x00; apdu.p2 = 0x00;
apdu.data= data; apdu.data= data;
apdu.datalen = apdu.lc = sizeof(data); apdu.datalen = apdu.lc = sizeof(data);
@ -848,7 +967,7 @@ etoken_generate_key(sc_card_t *card,
return r; 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; int r;
sc_apdu_t apdu; 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: case SC_CARDCTL_LIFECYCLE_SET:
return etoken_lifecycle_set(card, (int *) ptr); return etoken_lifecycle_set(card, (int *) ptr);
case SC_CARDCTL_GET_SERIALNR: 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; 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); 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 */ /* 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.init = etoken_init;
etoken_ops.finish = etoken_finish; etoken_ops.finish = etoken_finish;
etoken_ops.select_file = etoken_select_file; 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.set_security_env = etoken_set_security_env;
etoken_ops.restore_security_env = etoken_restore_security_env; etoken_ops.restore_security_env = etoken_restore_security_env;
etoken_ops.compute_signature = etoken_compute_signature; 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.check_sw = etoken_check_sw;
etoken_ops.card_ctl = etoken_card_ctl; etoken_ops.card_ctl = etoken_card_ctl;
etoken_ops.pin_cmd = etoken_pin_cmd; etoken_ops.pin_cmd = etoken_pin_cmd;
etoken_ops.logout = cardos_logout;
return &etoken_drv; return &etoken_drv;
} }

View File

@ -39,6 +39,7 @@ enum {
/* etoken driver */ /* etoken driver */
SC_CARD_TYPE_ETOKEN_BASE = 1000, SC_CARD_TYPE_ETOKEN_BASE = 1000,
SC_CARD_TYPE_ETOKEN_GENERIC, SC_CARD_TYPE_ETOKEN_GENERIC,
SC_CARD_TYPE_CARDOS_M4_2,
/* flex/cyberflex drivers */ /* flex/cyberflex drivers */
SC_CARD_TYPE_FLEX_BASE = 2000, SC_CARD_TYPE_FLEX_BASE = 2000,

View File

@ -1,6 +1,7 @@
/* /*
* CardOS specific operation for PKCS15 initialization * CardOS specific operation for PKCS15 initialization
* *
* Copyright (C) 2005 Nils Larsch <nils@larsch.net>
* Copyright (C) 2002 Olaf Kirch <okir@lst.de> * Copyright (C) 2002 Olaf Kirch <okir@lst.de>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
@ -29,6 +30,8 @@
#include <opensc/opensc.h> #include <opensc/opensc.h>
#include <opensc/cardctl.h> #include <opensc/cardctl.h>
#include <opensc/log.h> #include <opensc/log.h>
#include <opensc/cards.h>
#include <opensc/asn1.h>
#include "pkcs15-init.h" #include "pkcs15-init.h"
#include "profile.h" #include "profile.h"
@ -42,14 +45,6 @@ struct tlv {
unsigned char * current; unsigned char * current;
unsigned char * next; 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 * 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); const u8 *pin, size_t pin_len);
static int etoken_create_sec_env(sc_profile_t *, sc_card_t *, static int etoken_create_sec_env(sc_profile_t *, sc_card_t *,
unsigned int, unsigned int); 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 *, int, sc_pkcs15_prkey_info_t *,
struct sc_pkcs15_prkey_rsa *); struct sc_pkcs15_prkey_rsa *);
static int etoken_key_algorithm(unsigned int, int *); static int cardos_key_algorithm(unsigned int, size_t, int *);
static int etoken_extract_pubkey(sc_card_t *, int, static int cardos_extract_pubkey(sc_card_t *, sc_pkcs15_pubkey_t *,
u8, sc_pkcs15_bignum_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. /* Object IDs for PIN objects.
* SO PIN = 0x01, SO PUK = 0x02 * 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_PURE_SIG 0x8C
#define ETOKEN_ALGO_RSA_SIG_SHA1 0xC8 #define ETOKEN_ALGO_RSA_SIG_SHA1 0xC8
#define ETOKEN_ALGO_RSA_PURE_SIG_SHA1 0xCC #define ETOKEN_ALGO_RSA_PURE_SIG_SHA1 0xCC
#define ETOKEN_SIGN_RSA ETOKEN_ALGO_RSA_PURE_SIG #define CARDOS_ALGO_EXT_RSA_PURE 0x0a
#define ETOKEN_DECIPHER_RSA ETOKEN_ALGO_RSA_PURE #define CARDOS_ALGO_EXT_RSA_SIG_PURE 0x8a
#define ETOKEN_ALGO_PIN 0x87 #define ETOKEN_ALGO_PIN 0x87
static inline void 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) { if (r >= 0) {
r = etoken_store_pin(profile, card, r = etoken_store_pin(profile, card,
pin_info, puk_id, pin_info, puk_id, pin, pin_len);
pin, pin_len);
} }
return r; return r;
@ -267,17 +263,39 @@ etoken_store_key(sc_profile_t *profile, sc_card_t *card,
return SC_ERROR_NOT_SUPPORTED; 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 " sc_error(card->ctx, "CardOS does not support keys "
"that can both sign _and_ decrypt."); "that can both sign _and_ decrypt.");
return SC_ERROR_NOT_SUPPORTED; 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; 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 * 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_pkcs15_prkey_rsa key_obj;
struct sc_cardctl_etoken_genkey_info args; struct sc_cardctl_etoken_genkey_info args;
struct sc_file *temp; struct sc_file *temp;
u8 abignum[RSAKEY_MAX_SIZE]; u8 abignum[256];
unsigned int keybits; int algorithm, r, delete_it = 0, use_ext_rsa = 0;
int algorithm, r, delete_it = 0; size_t keybits, rsa_max_size;
if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) { if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA)
sc_error(card->ctx, "CardOS supports only RSA keys.");
return SC_ERROR_NOT_SUPPORTED; 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 " sc_error(card->ctx, "CardOS does not support keys "
"that can both sign _and_ decrypt."); "that can both sign _and_ decrypt.");
return SC_ERROR_NOT_SUPPORTED; 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) { if (sc_profile_get_file(profile, "tempfile", &temp) < 0) {
sc_error(card->ctx, "Profile doesn't define temporary file " sc_error(card->ctx, "Profile doesn't define temporary file "
"for key generation."); "for key generation.");
return SC_ERROR_NOT_SUPPORTED; 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) if ((r = sc_pkcs15init_create_file(profile, card, temp)) < 0)
goto out; goto out;
delete_it = 1; delete_it = 1;
/* Create a key object, initializing components to 0xff */ init_key_object(&key_obj, abignum, keybits >> 3);
memset(&key_obj, 0, sizeof(key_obj));
memset(abignum, 0xFF, sizeof(abignum)); r = cardos_put_key(profile, card, algorithm, key_info, &key_obj);
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);
if (r < 0) if (r < 0)
goto out; goto out;
@ -342,21 +359,12 @@ etoken_generate_key(sc_profile_t *profile, sc_card_t *card,
if (r < 0) if (r < 0)
goto out; goto out;
/* extract public key from file and delete it */ r = cardos_extract_pubkey(card, pubkey, temp, use_ext_rsa);
if ((r = sc_select_file(card, &temp->path, NULL)) < 0) out:
goto out; if (delete_it != 0)
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) {
sc_pkcs15init_rmdir(card, profile, temp); sc_pkcs15init_rmdir(card, profile, temp);
}
sc_file_free(temp); sc_file_free(temp);
if (r < 0) { if (r < 0) {
if (pubkey->u.rsa.modulus.data) if (pubkey->u.rsa.modulus.data)
free (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]; unsigned char pinpadded[16];
struct tlv tlv; struct tlv tlv;
unsigned int attempts, minlen, maxlen; unsigned int attempts, minlen, maxlen;
int r;
/* We need to do padding because pkcs15-lib.c does it. /* We need to do padding because pkcs15-lib.c does it.
* Would be nice to have a flag in the profile that says * Would be nice to have a flag in the profile that says
* "no padding required". */ * "no padding required". */
maxlen = MIN(profile->pin_maxlen, sizeof(pinpadded)); maxlen = MIN(profile->pin_maxlen, sizeof(pinpadded));
if (pin_len > maxlen) if (pin_len > maxlen) {
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); memcpy(pinpadded, pin, pin_len);
while (pin_len < maxlen) while (pin_len < maxlen)
pinpadded[pin_len++] = profile->pin_pad_char; 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); tlv_add(&tlv, 0xff);
/* DEK: not documented, no idea what it means */ /* 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, 0x00);
tlv_add(&tlv, minlen); /* minlen */ tlv_add(&tlv, minlen); /* minlen */
@ -437,6 +452,11 @@ etoken_store_pin(sc_profile_t *profile, sc_card_t *card,
args.data = buffer; args.data = buffer;
args.len = tlv_len(&tlv); 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); 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 sc_cardctl_etoken_obj_info args;
struct tlv tlv; struct tlv tlv;
unsigned char buffer[64]; unsigned char buffer[64];
int r;
tlv_init(&tlv, buffer, sizeof(buffer)); tlv_init(&tlv, buffer, sizeof(buffer));
tlv_next(&tlv, 0x83); tlv_next(&tlv, 0x83);
@ -469,6 +490,12 @@ etoken_create_sec_env(struct sc_profile *profile, sc_card_t *card,
args.data = buffer; args.data = buffer;
args.len = tlv_len(&tlv); 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); 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|\ #define USAGE_ANY_DECIPHER (SC_PKCS15_PRKEY_USAGE_DECRYPT|\
SC_PKCS15_PRKEY_USAGE_UNWRAP) SC_PKCS15_PRKEY_USAGE_UNWRAP)
static int static int cardos_key_algorithm(unsigned int usage, size_t keylen, int *algop)
etoken_key_algorithm(unsigned int usage, int *algop)
{ {
int sign = 0, decipher = 0; int sign = 0, decipher = 0;
if (usage & USAGE_ANY_SIGN) { 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; sign = 1;
} }
if (usage & USAGE_ANY_DECIPHER) { 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; decipher = 1;
} }
return (sign == decipher)? -1 : 0; 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 key_id, unsigned int pin_id,
unsigned int num, unsigned int num,
const u8 *data, size_t len, const u8 *data, size_t len,
int last) int last, int use_prefix)
{ {
struct sc_cardctl_etoken_obj_info args; struct sc_cardctl_etoken_obj_info args;
struct tlv tlv; struct tlv tlv;
unsigned char buffer[256]; unsigned char buffer[256];
#if SET_SM_BYTES
unsigned int n; unsigned int n;
#endif
int r;
/* Initialize the TLV encoder */ /* Initialize the TLV encoder */
tlv_init(&tlv, buffer, sizeof(buffer)); 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 USE */
tlv_add(&tlv, pin_id); /* AC CHANGE */ tlv_add(&tlv, pin_id); /* AC CHANGE */
tlv_add(&tlv, pin_id); /* UNKNOWN */ tlv_add(&tlv, pin_id); /* UNKNOWN */
/* The next 4 AC bytes are sent by the eToken run-time tlv_add(&tlv, 0); /* rfu */
* as well, but aren't documented anywhere. tlv_add(&tlv, 0); /* rfu */
* Key generation won't work without them, however. */ tlv_add(&tlv, 0); /* rfu */
tlv_add(&tlv, 0); #if 0
tlv_add(&tlv, 0); tlv_add(&tlv, pin_id); /* AC GENKEY */
tlv_add(&tlv, 0); #else
tlv_add(&tlv, 0); tlv_add(&tlv, 0);
#endif
#if SET_SM_BYTES
/* it shouldn't be necessary to set the default value */
/* SM bytes */ /* SM bytes */
tlv_next(&tlv, 0x8B); tlv_next(&tlv, 0x8B);
for (n = 0; n < 16; n++) for (n = 0; n < 16; n++)
tlv_add(&tlv, 0xFF); tlv_add(&tlv, 0xFF);
#endif
/* key component */ /* key component */
tlv_next(&tlv, 0x8f); tlv_next(&tlv, 0x8f);
tlv_add(&tlv, len+1); if (use_prefix != 0) {
tlv_add(&tlv, 0); tlv_add(&tlv, len+1);
tlv_add(&tlv, 0);
}
while (len--) while (len--)
tlv_add(&tlv, *data++); tlv_add(&tlv, *data++);
args.data = buffer; args.data = buffer;
args.len = tlv_len(&tlv); 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); return sc_card_ctl(card, SC_CARDCTL_ETOKEN_PUT_DATA_OCI, &args);
} }
static int static int cardos_put_key(sc_profile_t *profile, sc_card_t *card,
etoken_put_key(sc_profile_t *profile, sc_card_t *card, int algorithm, sc_pkcs15_prkey_info_t *key_info,
int algorithm, sc_pkcs15_prkey_info_t *key_info, struct sc_pkcs15_prkey_rsa *key)
struct sc_pkcs15_prkey_rsa *key)
{ {
int r, key_id, pin_id; 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) if (pin_id < 0)
pin_id = 0; pin_id = 0;
r = etoken_store_key_component(card, algorithm, key_id, pin_id, 0, if (key_info->modulus_length > 1024 && card->type == SC_CARD_TYPE_CARDOS_M4_2) {
key->modulus.data, key->modulus.len, 0); r = etoken_store_key_component(card, algorithm, key_id, pin_id, 0,
if (r < 0) key->p.data, key->p.len, 0, 0);
return r; if (r != SC_SUCCESS)
r = etoken_store_key_component(card, algorithm, key_id, pin_id, 1, return r;
key->d.data, key->d.len, 1); 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; 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 * Extract a key component from the public key file populated by
* GENERATE KEY PAIR * 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 static int
etoken_extract_pubkey(sc_card_t *card, int nr, u8 tag, etoken_extract_pubkey(sc_card_t *card, int nr, u8 tag,
sc_pkcs15_bignum_t *bn) sc_pkcs15_bignum_t *bn)
@ -603,12 +716,47 @@ etoken_extract_pubkey(sc_card_t *card, int nr, u8 tag,
return r; return r;
count = r - 4; count = r - 4;
if (count <= 0 || buf[0] != tag || buf[1] != count + 2 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; return SC_ERROR_INTERNAL;
bn->len = count; bn->len = count;
bn->data = (u8 *) malloc(count); bn->data = (u8 *) malloc(count);
if (bn->data == NULL)
return SC_ERROR_OUT_OF_MEMORY;
memcpy(bn->data, buf + 4, count); 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 = { static struct sc_pkcs15init_operations sc_pkcs15init_etoken_operations = {