add support for the Italian Incrypto34 smartcard;

patch supplied by Giuseppe AMATO <giuseppe.amato@st.com>


git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@2661 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
nils 2005-10-24 21:58:35 +00:00
parent b8981542fb
commit 90d1b0cc9c
14 changed files with 1771 additions and 5 deletions

View File

@ -28,6 +28,7 @@ libopensc_la_SOURCES = \
card-etoken.c card-tcos.c card-emv.c card-default.c \
card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \
card-oberthur.c card-belpic.c card-atrust-acos.c \
card-incrypto34.c \
\
pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \
pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafe.c \

View File

@ -26,6 +26,7 @@ OBJECTS = \
card-etoken.obj card-tcos.obj card-emv.obj card-default.obj \
card-mcrd.obj card-starcos.obj card-openpgp.obj card-jcop.obj \
card-oberthur.obj card-belpic.obj card-atrust-acos.obj \
card-incrypto34.obj \
\
pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \
pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj \

View File

@ -0,0 +1,936 @@
/*
* card-incrypto34.c: Support for Incard Incrypto34 based cards and tokens
* (for example Italian CNS)
*
* Copyright (C) 2005 ST Incard srl, Giuseppe Amato <giuseppe dot amato at st dot com>
* Copyright (C) 2002 Andreas Jellinghaus <aj@dungeon.inka.de>
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "internal.h"
#include "cardctl.h"
#include <ctype.h>
#include <string.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.
* Either coincidence, or a known problem. */
#define INCRYPTO34_MAX_PAYLOAD 120
static const struct sc_card_operations *iso_ops = NULL;
struct sc_card_operations incrypto34_ops;
struct sc_card_driver incrypto34_drv = {
"Incard Incripto34",
"incrypto34",
&incrypto34_ops
};
static struct sc_atr_table incrypto34_atrs[] = {
/* Italian CNS (similar to a eID) card*/
{ "3b:ff:18:00:ff:81:31:fe:55:00:6b:02:09:02:00:01:01:01:43:4e:53:10:31:80:9f", NULL, NULL, SC_CARD_TYPE_INCRYPTO34_GENERIC, 0, NULL },
{ "3b:ff:18:00:ff:81:31:fe:55:00:6b:02:09:02:00:01:01:01:44:53:44:10:31:80:92", NULL, NULL, SC_CARD_TYPE_INCRYPTO34_GENERIC, 0, NULL },
{ NULL, NULL, NULL, 0, 0, NULL }
};
int incrypto34_finish(struct sc_card *card)
{
return 0;
}
int incrypto34_match_card(struct sc_card *card)
{
int i;
i = _sc_match_atr(card, incrypto34_atrs, &card->type);
if (i < 0)
return 0;
return 1;
}
int incrypto34_init(sc_card_t *card)
{
unsigned long flags;
card->name = "Incrypto34";
card->cla = 0x00;
/* Set up algorithm info. */
flags = SC_ALGORITHM_NEED_USAGE
| SC_ALGORITHM_RSA_RAW
| SC_ALGORITHM_RSA_HASH_NONE
| SC_ALGORITHM_ONBOARD_KEY_GEN
;
_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);
return 0;
}
static const struct sc_card_error incrypto34_errors[] = {
/* some error inside the card */
/* i.e. nothing you can do */
{ 0x6581, SC_ERROR_MEMORY_FAILURE, "EEPROM error; command aborted"},
{ 0x6fff, SC_ERROR_CARD_CMD_FAILED, "internal assertion error"},
{ 0x6700, SC_ERROR_WRONG_LENGTH, "LC invalid"},
{ 0x6985, SC_ERROR_CARD_CMD_FAILED, "no random number available"},
{ 0x6f81, SC_ERROR_CARD_CMD_FAILED, "file invalid, maybe checksum error"},
{ 0x6f82, SC_ERROR_CARD_CMD_FAILED, "not enough memory in xram"},
{ 0x6f84, SC_ERROR_CARD_CMD_FAILED, "general protection fault"},
/* the card doesn't now thic combination of ins+cla+p1+p2 */
/* i.e. command will never work */
{ 0x6881, SC_ERROR_NO_CARD_SUPPORT, "logical channel not supported"},
{ 0x6a86, SC_ERROR_INCORRECT_PARAMETERS,"p1/p2 invalid"},
{ 0x6d00, SC_ERROR_INS_NOT_SUPPORTED, "ins invalid"},
{ 0x6e00, SC_ERROR_CLASS_NOT_SUPPORTED, "class invalid (hi nibble)"},
/* known command, but incorrectly used */
/* i.e. command could work, but you need to change something */
{ 0x6981, SC_ERROR_CARD_CMD_FAILED, "command cannot be used for file structure"},
{ 0x6a80, SC_ERROR_INCORRECT_PARAMETERS,"invalid parameters in data field"},
{ 0x6a81, SC_ERROR_NOT_SUPPORTED, "function/mode not supported"},
{ 0x6a85, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit the tlv structure"},
{ 0x6986, SC_ERROR_INCORRECT_PARAMETERS,"no current ef selected"},
{ 0x6a87, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit p1/p2"},
{ 0x6c00, SC_ERROR_WRONG_LENGTH, "le does not fit the data to be sent"},
{ 0x6f83, SC_ERROR_CARD_CMD_FAILED, "command must not be used in transaction"},
/* (something) not found */
{ 0x6987, SC_ERROR_INCORRECT_PARAMETERS,"key object for sm not found"},
{ 0x6f86, SC_ERROR_CARD_CMD_FAILED, "key object not found"},
{ 0x6a82, SC_ERROR_FILE_NOT_FOUND, "file not found"},
{ 0x6a83, SC_ERROR_RECORD_NOT_FOUND, "record not found"},
{ 0x6a88, SC_ERROR_CARD_CMD_FAILED, "object not found"},
{ 0x6a89, SC_ERROR_FILE_ALREADY_EXISTS, "file/object already exists"},
/* (something) invalid */
{ 0x6884, SC_ERROR_CARD_CMD_FAILED, "chaining error"},
{ 0x6984, SC_ERROR_CARD_CMD_FAILED, "bs object has invalid format"},
{ 0x6988, SC_ERROR_INCORRECT_PARAMETERS,"key object used for sm has invalid format"},
/* (something) deactivated */
{ 0x6283, SC_ERROR_CARD_CMD_FAILED, "file is deactivated" },
{ 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "bs object blocked"},
/* access denied */
{ 0x6300, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"authentication failed"},
{ 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"required access right not granted"},
/* other errors */
{ 0x6a84, SC_ERROR_CARD_CMD_FAILED, "not enough memory"},
/* command ok, execution failed */
{ 0x6f00, SC_ERROR_CARD_CMD_FAILED, "technical error (see incrypto34 developers guide)"},
/* no error, maybe a note */
{ 0x9000, SC_NO_ERROR, NULL},
{ 0x9001, SC_NO_ERROR, "success, but eeprom weakness detected"},
{ 0x9850, SC_NO_ERROR, "over/underflow useing in/decrease"}
};
static int incrypto34_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2)
{
const int err_count = sizeof(incrypto34_errors)/sizeof(incrypto34_errors[0]);
int i;
for (i = 0; i < err_count; i++) {
if (incrypto34_errors[i].SWs == ((sw1 << 8) | sw2)) {
if ( incrypto34_errors[i].errorstr )
sc_error(card->ctx, "%s\n",
incrypto34_errors[i].errorstr);
return incrypto34_errors[i].errorno;
}
}
sc_error(card->ctx, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2);
return SC_ERROR_CARD_CMD_FAILED;
}
int incrypto34_list_files(sc_card_t *card, u8 *buf, size_t buflen)
{
sc_apdu_t apdu;
u8 rbuf[256];
int r;
size_t fids;
u8 offset;
SC_FUNC_CALLED(card->ctx, 1);
fids=0;
offset=0;
/* INS 0xFC: SCAN DF*/
/* P1 0x00: list both DF and EF */
/* P2 0x00/0x01: first/next element */
/* LE 0x03*/
/*
returns 3 bytes: FILE_TYPE + FID_HI_BYTE + FID_LO_BYTE
*/
get_next_part:
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xFC, 0x00, offset?0x01:0x00);
apdu.cla = 0xB0;
apdu.le = 256;
apdu.resplen = 3;
apdu.resp = rbuf;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
if (apdu.sw1 == 0x6a && apdu.sw2 == 0x82)
goto end; // no more files
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "DIRECTORY command returned error");
if (apdu.resplen >= 3
&& ((rbuf[0] >= 0x01 && rbuf[0] <= 0x07) || 0x38 == rbuf[0])
&& fids + 2 >= buflen)
{
buf[fids++] = rbuf[1];
buf[fids++] = rbuf[2];
}
++offset;
goto get_next_part;
end:
r = fids;
SC_FUNC_RETURN(card->ctx, 1, r);
}
static void add_acl_entry(sc_file_t *file, int op, u8 byte)
{
unsigned int method, key_ref = SC_AC_KEY_REF_NONE;
switch (byte) {
case 0x00:
method = SC_AC_NONE;
break;
case 0xFF:
method = SC_AC_NEVER;
break;
default:
if (byte > 0x7F) {
method = SC_AC_UNKNOWN;
} else {
method = SC_AC_CHV;
key_ref = byte;
}
break;
}
sc_file_add_acl_entry(file, op, method, key_ref);
}
static int acl_to_byte(const sc_acl_entry_t *e)
{
if (e != NULL) {
switch (e->method) {
case SC_AC_NONE:
return 0x00;
case SC_AC_NEVER:
return 0xFF;
case SC_AC_CHV:
case SC_AC_TERM:
case SC_AC_AUT:
if (e->key_ref == SC_AC_KEY_REF_NONE)
return -1;
if (e->key_ref > 0x7F)
return -1;
return e->key_ref;
}
}
return 0x00;
}
static const int df_acl[9] = {
-1, /* LCYCLE (life cycle change) */
SC_AC_OP_UPDATE, /* UPDATE Objects */
-1, /* APPEND Objects */
SC_AC_OP_INVALIDATE, /* DF */
SC_AC_OP_REHABILITATE, /* DF */
SC_AC_OP_DELETE, /* DF */
-1, /* ADMIN DF */
SC_AC_OP_CREATE, /* Files */
-1 /* Reserved */
};
static const int ef_acl[9] = {
SC_AC_OP_READ, /* Data */
SC_AC_OP_UPDATE, /* Data (write file content) */
SC_AC_OP_WRITE, /* */
SC_AC_OP_INVALIDATE, /* EF */
SC_AC_OP_REHABILITATE, /* EF */
SC_AC_OP_ERASE, /* (delete) EF */
/* XXX: ADMIN should be an ACL type of its own, or mapped
* to erase */
-1, /* ADMIN EF (modify meta information?) */
-1, /* INC (-> cylic fixed files) */
-1 /* DEC */
};
static void parse_sec_attr(sc_file_t *file, const u8 *buf, size_t len)
{
size_t i;
const int *idx;
idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl;
/* acl defaults to 0xFF if unspecified */
for (i = 0; i < 9; i++)
if (idx[i] != -1)
add_acl_entry(file, idx[i], (u8)((i < len) ? buf[i] : 0xFF));
}
static int incrypto34_select_file(sc_card_t *card,
const sc_path_t *in_path,
sc_file_t **file)
{
int r;
SC_FUNC_CALLED(card->ctx, 1);
r = iso_ops->select_file(card, in_path, file);
if (r >= 0 && file)
parse_sec_attr((*file), (*file)->sec_attr, (*file)->sec_attr_len);
SC_FUNC_RETURN(card->ctx, 1, r);
}
static int incrypto34_create_file(sc_card_t *card, sc_file_t *file)
{
int r, i, byte;
const int *idx;
u8 acl[9], type[3], status[3];
if (card->ctx->debug >= 1) {
char pbuf[128+1];
size_t n;
for (n = 0; n < file->path.len; n++) {
snprintf(pbuf + 2 * n, sizeof(pbuf) - 2 * n,
"%02X", file->path.value[n]);
}
sc_debug(card->ctx, "incrypto34_create_file(%s)\n", pbuf);
}
if (file->type_attr_len == 0) {
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;
}
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;
/* No idea what this means, but it
* seems to be required for key
* generation. */
case SC_FILE_EF_LINEAR_VARIABLE_TLV:
type[1] = 0xff;
default:
type[0] |= file->ef_structure & 7;
break;
}
}
r = sc_file_set_type_attr(file, type, sizeof(type));
if (r)
goto out;
}
if (file->prop_attr_len == 0) {
status[0] = 0x01;
if (file->type == SC_FILE_TYPE_DF) {
status[1] = file->size >> 8;
status[2] = file->size;
} else {
status[1] = status[2] = 0x00; /* not used */
}
r = sc_file_set_prop_attr(file, status, sizeof(status));
if (r)
goto out;
}
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;
}
r = iso_ops->create_file(card, file);
/* FIXME: if this is a DF and there's an AID, set it here
* using PUT_DATA_FCI */
out: SC_FUNC_RETURN(card->ctx, 1, r);
}
/*
* Restore the indicated SE
*/
static int
incrypto34_restore_security_env(sc_card_t *card, int se_num)
{
sc_apdu_t apdu;
int r;
SC_FUNC_CALLED(card->ctx, 1);
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0xF3, se_num);
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, "Card returned error");
SC_FUNC_RETURN(card->ctx, 1, r);
}
/*
* Set the security context
* Things get a little messy here. It seems you cannot do any
* crypto without a security environment - but there isn't really
* a way to specify the security environment in PKCS15.
* What I'm doing here (for now) is to assume that for a key
* object with ID 0xNN there is always a corresponding SE object
* with the same ID.
* XXX Need to find out how the Aladdin drivers do it.
*/
static int
incrypto34_set_security_env(sc_card_t *card,
const sc_security_env_t *env,
int se_num)
{
sc_apdu_t apdu;
u8 data[3];
int key_id, r;
assert(card != NULL && env != NULL);
if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT)
|| env->key_ref_len != 1) {
sc_error(card->ctx, "No or invalid key reference\n");
return SC_ERROR_INVALID_ARGUMENTS;
}
key_id = env->key_ref[0];
r = incrypto34_restore_security_env(card, 1);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF1, 0);
switch (env->operation) {
case SC_SEC_OPERATION_DECIPHER:
apdu.p2 = 0xB8;
break;
case SC_SEC_OPERATION_SIGN:
apdu.p2 = 0xB6;
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
data[0] = 0x83;
data[1] = 0x01;
data[2] = key_id;
apdu.lc = apdu.datalen = 3;
apdu.data = data;
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, "Card returned error");
SC_FUNC_RETURN(card->ctx, 1, r);
}
/*
* Compute digital signature
*/
/* internal function to do the actual signature computation */
static int
do_compute_signature(sc_card_t *card, const u8 *data, size_t datalen,
u8 *out, size_t outlen)
{
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);
memcpy(sbuf, data, datalen);
apdu.data = sbuf;
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));
}
static int
incrypto34_compute_signature(sc_card_t *card, const u8 *data, size_t datalen,
u8 *out, size_t outlen)
{
int r;
u8 buf[SC_MAX_APDU_BUFFER_SIZE];
size_t buf_len = sizeof(buf), tmp_len = buf_len;
sc_context_t *ctx;
assert(card != NULL && data != NULL && out != NULL);
ctx = card->ctx;
SC_FUNC_CALLED(ctx, 1);
if (datalen > 255)
SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
if (outlen < datalen)
SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_BUFFER_TOO_SMALL);
outlen = datalen;
/* XXX As we don't know what operations are allowed with a
* certain key, let's try RSA_PURE etc. and see which operation
* succeeds (this is not really beautiful, but currently the
* only way I see) -- Nils
*/
if (ctx->debug >= 3)
sc_debug(ctx, "trying RSA_PURE_SIG (padded DigestInfo)\n");
ctx->suppress_errors++;
r = do_compute_signature(card, data, datalen, out, outlen);
ctx->suppress_errors--;
if (r >= SC_SUCCESS)
SC_FUNC_RETURN(ctx, 4, r);
if (ctx->debug >= 3)
sc_debug(ctx, "trying RSA_SIG (just the DigestInfo)\n");
/* remove padding: first try pkcs1 bt01 padding */
r = sc_pkcs1_strip_01_padding(data, datalen, buf, &tmp_len);
if (r != SC_SUCCESS) {
/* no pkcs1 bt01 padding => let's try zero padding */
tmp_len = buf_len;
r = sc_strip_zero_padding(data, datalen, buf, &tmp_len);
if (r != SC_SUCCESS)
SC_FUNC_RETURN(ctx, 4, r);
}
ctx->suppress_errors++;
r = do_compute_signature(card, buf, tmp_len, out, outlen);
ctx->suppress_errors--;
if (r >= SC_SUCCESS)
SC_FUNC_RETURN(ctx, 4, r);
if (ctx->debug >= 3)
sc_debug(ctx, "trying to sign raw hash value\n");
r = sc_pkcs1_strip_digest_info_prefix(NULL,buf,tmp_len,buf,&buf_len);
if (r != SC_SUCCESS)
SC_FUNC_RETURN(ctx, 4, r);
return do_compute_signature(card, buf, buf_len, out, outlen);
}
static int
incrypto34_lifecycle_get(sc_card_t *card, int *mode)
{
sc_apdu_t apdu;
u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
int r;
SC_FUNC_CALLED(card->ctx, 1);
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 01, 0x83);
apdu.cla = 0x00;
apdu.le = 256;
apdu.resplen = sizeof(rbuf);
apdu.resp = rbuf;
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, "Card returned error");
if (apdu.resplen < 1) {
SC_TEST_RET(card->ctx, r, "Lifecycle byte not in response");
}
r = SC_SUCCESS;
switch (rbuf[0]) {
case 0x10:
*mode = SC_CARDCTRL_LIFECYCLE_USER;
break;
case 0x20:
*mode = SC_CARDCTRL_LIFECYCLE_ADMIN;
break;
case 0x34: /* MANUFACTURING */
*mode = SC_CARDCTRL_LIFECYCLE_OTHER;
break;
default:
sc_error(card->ctx, "Unknown lifecycle byte %d", rbuf[0]);
r = SC_ERROR_INTERNAL;
}
SC_FUNC_RETURN(card->ctx, 1, r);
}
#if 0
static int
incrypto34_lifecycle_set(sc_card_t *card, int *mode)
{
sc_apdu_t apdu;
int r;
int current;
int target;
SC_FUNC_CALLED(card->ctx, 1);
target = *mode;
r = incrypto34_lifecycle_get(card, &current);
if (r != SC_SUCCESS)
return r;
if (current == target || current == SC_CARDCTRL_LIFECYCLE_OTHER)
return SC_SUCCESS;
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x10, 0, 0);
apdu.cla = 0x80;
apdu.le = 0;
apdu.resplen = 0;
apdu.resp = NULL;
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, "Card returned error");
SC_FUNC_RETURN(card->ctx, 1, r);
}
#endif
static int
incrypto34_put_data_oci(sc_card_t *card,
struct sc_cardctl_incrypto34_obj_info *args)
{
sc_apdu_t apdu;
int r;
SC_FUNC_CALLED(card->ctx, 1);
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.cla = 0x00;
apdu.ins = 0xda;
apdu.p1 = 0x01;
apdu.p2 = 0x6e;
apdu.lc = args->len;
apdu.data = args->data;
apdu.datalen = args->len;
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, "Card returned error");
SC_FUNC_RETURN(card->ctx, 1, r);
}
static int
incrypto34_change_key_data(struct sc_card *card,
struct sc_cardctl_incrypto34_obj_info *args)
{
struct sc_apdu apdu;
int r;
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.cla = 0x90;
apdu.ins = 0x24;
apdu.p1 = args->key_class;
apdu.p2 = args->key_id;
apdu.lc = args->len;
apdu.data = args->data;
apdu.datalen = args->len;
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, "Card returned error");
return r;
}
static int
incrypto34_put_data_seci(sc_card_t *card,
struct sc_cardctl_incrypto34_obj_info *args)
{
sc_apdu_t apdu;
int r;
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.cla = 0x00;
apdu.ins = 0xda;
apdu.p1 = 0x01;
apdu.p2 = 0x6d;
apdu.lc = args->len;
apdu.data = args->data;
apdu.datalen = args->len;
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, "Card returned error");
return r;
}
static int
incrypto34_generate_key(sc_card_t *card,
struct sc_cardctl_incrypto34_genkey_info *args)
{
sc_apdu_t apdu;
u8 data[8];
int r;
data[0] = 0x20; /* store as PSO object */
data[1] = args->key_id;
data[2] = args->fid >> 8;
data[3] = args->fid & 0xff;
data[4] = 0; /* additional Rabin Miller tests */
data[5] = 0x10; /* length difference between p, q (bits) */
data[6] = 0; /* default length of exponent, MSB */
data[7] = 0x20; /* default length of exponent, LSB */
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_3_SHORT;
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);
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, "GENERATE_KEY failed");
return r;
}
static int
incrypto34_erase_files(sc_card_t *card)
{
sc_apdu_t apdu;
int r;
static u8 pCreateAtrFile[] = {
0x62, 0x1b,
0x80, 0x02, 0x00, 0x1e,
0x82, 0x03, 0x01, 0xff, 0xff,
0x83, 0x02, 0x2f, 0x01,
0x85, 0x01, 0x01,
0x86, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static u8 pWriteAtr[] = { 0x19, 0x3b, 0xff,
0x18, 0x00, 0xff, 0x81, 0x31, 0xfe, 0x55, 0x00,
0x6b, 0x02, 0x09, 0x02, 0x00, 0x01, 0x01, 0x01,
0x43, 0x4e, 0x53, 0x10, 0x31, 0x80, 0x9f };
static u8 pCreateEF_DIR_ADOFile[] = { 0x6F, 0x1D,
0x83, 0x02,
0xFD, 0x01, 0x85, 0x03, 0x01, 0xff, 0xff,
0x81, 0x02,
0x00, 0x64,
0x82, 0x03, 0x05, 0xff, 0xff,
0x86, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
/* Erasing Filesystem */
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xf5, 0, 0);
apdu.cla = 0xb0;
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, "Card returned error Erasing Filesystem");
/* Creating ATR file*/
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe0, 0, 0);
apdu.data = pCreateAtrFile;
apdu.datalen = apdu.lc = sizeof(pCreateAtrFile);
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, "Card returned error Creating ATR file");
/* Filling ATR file*/
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xd6, 0, 0);
apdu.data = pWriteAtr;
apdu.datalen = apdu.lc = sizeof(pWriteAtr);
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, "Card returned error Filling ATR file");
/* Creating DIR-ADO file*/
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xe0, 0, 0);
apdu.data = pCreateEF_DIR_ADOFile;
apdu.datalen = apdu.lc = sizeof(pCreateEF_DIR_ADOFile);
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, "Card returned error Creating DIR-ADO file");
return r;
}
static int
incrypto34_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
{
switch (cmd) {
case SC_CARDCTL_INCRYPTO34_PUT_DATA_FCI:
break;
case SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI:
return incrypto34_put_data_oci(card,
(struct sc_cardctl_incrypto34_obj_info *) ptr);
break;
case SC_CARDCTL_INCRYPTO34_PUT_DATA_SECI:
return incrypto34_put_data_seci(card,
(struct sc_cardctl_incrypto34_obj_info *) ptr);
break;
case SC_CARDCTL_INCRYPTO34_GENERATE_KEY:
return incrypto34_generate_key(card,
(struct sc_cardctl_incrypto34_genkey_info *) ptr);
case SC_CARDCTL_LIFECYCLE_GET:
return incrypto34_lifecycle_get(card, (int *) ptr);
case SC_CARDCTL_LIFECYCLE_SET:
return 0;
case SC_CARDCTL_INCRYPTO34_CHANGE_KEY_DATA:
return incrypto34_change_key_data(card, (struct sc_cardctl_incrypto34_obj_info*) ptr);
case SC_CARDCTL_INCRYPTO34_ERASE_FILES:
return incrypto34_erase_files(card);
}
return SC_ERROR_NOT_SUPPORTED;
}
/*
* The 0x80 thing tells the card it's okay to search parent
* directories as well for the referenced object.
* Unfortunately, it doesn't seem to work without this flag :-/
*/
static int
incrypto34_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data,
int *tries_left)
{
data->flags |= SC_PIN_CMD_NEED_PADDING;
data->pin_reference |= 0x80;
/* FIXME: the following values depend on what pin length was
* used when creating the BS objects */
if (data->pin1.max_length == 0)
data->pin1.max_length = 8;
if (data->pin2.max_length == 0)
data->pin2.max_length = 8;
return iso_ops->pin_cmd(card, data, tries_left);
}
static struct sc_card_driver * sc_get_driver(void)
{
if (iso_ops == NULL)
iso_ops = sc_get_iso7816_driver()->ops;
incrypto34_ops = *iso_ops;
incrypto34_ops.match_card = incrypto34_match_card;
incrypto34_ops.init = incrypto34_init;
incrypto34_ops.finish = incrypto34_finish;
incrypto34_ops.select_file = incrypto34_select_file;
incrypto34_ops.create_file = incrypto34_create_file;
incrypto34_ops.set_security_env = incrypto34_set_security_env;
incrypto34_ops.restore_security_env = incrypto34_restore_security_env;
incrypto34_ops.compute_signature = incrypto34_compute_signature;
incrypto34_ops.list_files = incrypto34_list_files;
incrypto34_ops.check_sw = incrypto34_check_sw;
incrypto34_ops.card_ctl = incrypto34_card_ctl;
incrypto34_ops.pin_cmd = incrypto34_pin_cmd;
return &incrypto34_drv;
}
#if 1
struct sc_card_driver * sc_get_incrypto34_driver(void)
{
return sc_get_driver();
}
#endif

View File

@ -109,7 +109,18 @@ enum {
SC_CARDCTL_SETCOS_PUTDATA,
SC_CARDCTL_SETCOS_GETDATA,
SC_CARDCTL_SETCOS_GENERATE_STORE_KEY,
SC_CARDCTL_SETCOS_ACTIVATE_FILE
SC_CARDCTL_SETCOS_ACTIVATE_FILE,
/*
* Incrypto34 specific calls
*/
SC_CARDCTL_INCRYPTO34_BASE = _CTL_PREFIX('I', '3', '4'),
SC_CARDCTL_INCRYPTO34_PUT_DATA_FCI,
SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI,
SC_CARDCTL_INCRYPTO34_PUT_DATA_SECI,
SC_CARDCTL_INCRYPTO34_GENERATE_KEY,
SC_CARDCTL_INCRYPTO34_CHANGE_KEY_DATA,
SC_CARDCTL_INCRYPTO34_ERASE_FILES
};
enum {
@ -199,6 +210,22 @@ struct sc_cardctl_etoken_genkey_info {
unsigned short fid;
};
/*
* Incrypto34 PIN info
*/
struct sc_cardctl_incrypto34_obj_info {
u8 * data;
size_t len;
unsigned int key_id;
unsigned int key_class;
};
struct sc_cardctl_incrypto34_genkey_info {
unsigned int key_id;
unsigned int key_bits;
unsigned short fid;
};
/*
* Cryptoflex info
*/

View File

@ -102,7 +102,11 @@ enum {
/* belpic driver */
SC_CARD_TYPE_BELPIC_BASE = 12000,
SC_CARD_TYPE_BELPIC_GENERIC,
SC_CARD_TYPE_BELPIC_EID
SC_CARD_TYPE_BELPIC_EID,
/* incrypto34 driver */
SC_CARD_TYPE_INCRYPTO34_BASE = 13000,
SC_CARD_TYPE_INCRYPTO34_GENERIC
};
#ifdef __cplusplus

View File

@ -76,6 +76,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = {
{ "belpic", (void *(*)(void)) sc_get_belpic_driver },
{ "atrust-acos",(void *(*)(void))sc_get_atrust_acos_driver },
{ "emv", (void *(*)(void)) sc_get_emv_driver },
{ "incrypto34", (void *(*)(void)) sc_get_incrypto34_driver },
/* The default driver should be last, as it handles all the
* unrecognized cards. */
{ "default", (void *(*)(void)) sc_get_default_driver },

View File

@ -34,7 +34,7 @@ extern "C" {
#define SC_LOG_TYPE_DEBUG 2
/* You can't do #ifndef __FUNCTION__ */
#if !defined(__GNUC__) && !defined(__IBMC__)
#if !defined(__GNUC__) && !defined(__IBMC__) && !(defined(_MSC_VER) && (_MSC_VER >= 1300))
#define __FUNCTION__ NULL
#endif

View File

@ -944,6 +944,7 @@ extern sc_card_driver_t *sc_get_jcop_driver(void);
extern sc_card_driver_t *sc_get_oberthur_driver(void);
extern sc_card_driver_t *sc_get_belpic_driver(void);
extern sc_card_driver_t *sc_get_atrust_acos_driver(void);
extern sc_card_driver_t *sc_get_incrypto34_driver(void);
#ifdef __cplusplus
}

View File

@ -13,6 +13,7 @@ PROFILES = \
gpk.profile \
miocos.profile \
etoken.profile \
incrypto34.profile \
jcop.profile \
oberthur.profile \
starcos.profile \
@ -27,7 +28,7 @@ libpkcs15init_la_SOURCES = \
pkcs15-lib.c profile.c keycache.c \
pkcs15-gpk.c pkcs15-miocos.c pkcs15-cflex.c \
pkcs15-etoken.c pkcs15-jcop.c pkcs15-starcos.c \
pkcs15-oberthur.c pkcs15-setcos.c
pkcs15-oberthur.c pkcs15-setcos.c pkcs15-incrypto34.c
libpkcs15init_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@

View File

@ -8,7 +8,7 @@ HEADERSDIR = $(TOPDIR)\src\include\opensc
OBJECTS = profile.obj pkcs15-lib.obj keycache.obj \
pkcs15-miocos.obj pkcs15-gpk.obj pkcs15-cflex.obj \
pkcs15-etoken.obj pkcs15-jcop.obj pkcs15-starcos.obj \
pkcs15-oberthur.obj pkcs15-setcos.obj
pkcs15-oberthur.obj pkcs15-setcos.obj pkcs15-incrypto34.obj
all: install-headers $(TARGET)

View File

@ -0,0 +1,106 @@
#
# PKCS15 r/w profile for Incard's Incrypto34 (aka Italian CIE/CNS)
#
cardinfo {
max-pin-length = 8;
pin-encoding = ascii-numeric;
pin-pad-char = 0x00;
}
# Define reasonable limits for PINs and PUK
# We set the reference for SO pin+puk here, because
# those are hard-coded (if a PUK us assigned).
PIN so-pin {
reference = 0;
}
PIN so-puk {
reference = 1;
}
PIN user-pin {
attempts = 3;
}
PIN user-puk {
attempts = 10;
}
# Additional filesystem info.
# This is added to the file system info specified in the
# main profile.
filesystem {
DF MF {
DF PKCS15-AppDF {
size = 2048;
# Prevent unauthorized updates of basic security
# objects via PUT DATA OCI.
ACL = UPDATE=NEVER;
# Bump the size of the EF(PrKDF) - with split
# keys, we may need a little more room.
EF PKCS15-PrKDF {
size = 256;
}
# 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 {
# This is a dummy entry - pkcs15-init insists that
# this is present
EF private-key {
file-id = FFFF;
}
EF public-key {
file-id = 3003;
structure = transparent;
ACL = *=NEVER,
READ=NONE,
UPDATE=$PIN,
ERASE=$PIN;
}
# Certificate template
EF certificate {
file-id = 3104;
structure = transparent;
ACL = *=NEVER,
READ=NONE,
UPDATE=$PIN,
ERASE=$PIN;
}
# Extractable private keys are stored in transparent EFs.
# Encryption of the content is performed by libopensc.
EF extractable-key {
file-id = 3201;
structure = transparent;
ACL = *=NEVER,
READ=$PIN,
UPDATE=$PIN,
ERASE=$PIN;
}
# data objects are stored in transparent EFs.
EF data {
file-id = 3302;
structure = transparent;
ACL = *=NEVER,
READ=NONE,
UPDATE=$PIN,
ERASE=$PIN;
}
}
# This is needed when generating a key on-card.
EF tempfile {
file-id = 7EAD;
structure = linear-variable-tlv;
ACL = *=NONE;
size = 512;
}
}
}
}

View File

@ -0,0 +1,686 @@
/*
* Incrypto34 specific operation for PKCS15 initialization
*
* Copyright (C) 2005 ST Incard srl, Giuseppe Amato <giuseppe dot amato at st dot com>
* Copyright (C) 2002 Olaf Kirch <okir@lst.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include <opensc/opensc.h>
#include <opensc/cardctl.h>
#include <opensc/log.h>
#include "pkcs15-init.h"
#include "profile.h"
#ifndef MIN
# define MIN(a, b) (((a) < (b))? (a) : (b))
#endif
struct tlv {
unsigned char * base;
unsigned char * end;
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
*/
static int incrypto34_store_pin(sc_profile_t *profile, sc_card_t *card,
sc_pkcs15_pin_info_t *pin_info, int puk_id,
const u8 *pin, size_t pin_len);
static int incrypto34_create_sec_env(sc_profile_t *, sc_card_t *,
unsigned int, unsigned int);
static int incrypto34_put_key(struct sc_profile *, struct sc_card *,
int, sc_pkcs15_prkey_info_t *,
struct sc_pkcs15_prkey_rsa *);
static int incrypto34_key_algorithm(unsigned int, int *);
static int incrypto34_extract_pubkey(sc_card_t *, int,
u8, sc_pkcs15_bignum_t *);
/* Object IDs for PIN objects.
* SO PIN = 0x01, SO PUK = 0x02
* each user pin is 2*N+1, each corresponding PUK is 2*N+2
*/
#define INCRYPTO34_PIN_ID_MIN 1
#define INCRYPTO34_PIN_ID_MAX 15
#define INCRYPTO34_KEY_ID_MIN 16
#define INCRYPTO34_KEY_ID_MAX 31
#define INCRYPTO34_AC_NEVER 0xFF
#define INCRYPTO34_ALGO_RSA 0x08
#define INCRYPTO34_ALGO_RSA_PURE 0x0C
#define INCRYPTO34_ALGO_RSA_SIG 0x88
#define INCRYPTO34_ALGO_RSA_PURE_SIG 0x8C
#define INCRYPTO34_ALGO_RSA_SIG_SHA1 0xC8
#define INCRYPTO34_ALGO_RSA_PURE_SIG_SHA1 0xCC
#define INCRYPTO34_SIGN_RSA INCRYPTO34_ALGO_RSA_SIG
#define INCRYPTO34_DECIPHER_RSA INCRYPTO34_ALGO_RSA_PURE
#define INCRYPTO34_ALGO_PIN 0x87
static inline void
tlv_init(struct tlv *tlv, u8 *base, size_t size)
{
tlv->base = base;
tlv->end = base + size;
tlv->current = tlv->next = base;
}
static inline void
tlv_next(struct tlv *tlv, u8 tag)
{
assert(tlv->next + 2 < tlv->end);
tlv->current = tlv->next;
*(tlv->next++) = tag;
*(tlv->next++) = 0;
}
static inline void
tlv_add(struct tlv *tlv, u8 val)
{
assert(tlv->next + 1 < tlv->end);
*(tlv->next++) = val;
tlv->current[1]++;
}
static size_t
tlv_len(struct tlv *tlv)
{
return tlv->next - tlv->base;
}
/*
* Try to delete pkcs15 structure
* This is not quite the same as erasing the whole token, but
* it's close enough to be useful.
*/
static int
incrypto34_erase(struct sc_profile *profile, sc_card_t *card)
{
int r;
struct sc_file *file;
struct sc_path path;
memset(&file, 0, sizeof(file));
sc_format_path("3F00", &path);
if ((r = sc_select_file(card, &path, &file)) < 0)
return r;
if ((r = sc_pkcs15init_authenticate(profile, card, file, SC_AC_OP_DELETE)) < 0)
return sc_pkcs15init_erase_card_recursively(card, profile, -1);
else
return sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_ERASE_FILES, NULL);
}
/*
* Create the Application DF
*/
static int
incrypto34_create_dir(sc_profile_t *profile, sc_card_t *card, sc_file_t *df)
{
int r;
/* Create the application DF */
if ((r = sc_pkcs15init_create_file(profile, card, df)) < 0)
return r;
if ((r = sc_select_file(card, &df->path, NULL)) < 0)
return r;
/* Create a security environment for this DF.
*/
if ((r = incrypto34_create_sec_env(profile, card, 0x01, 0x00)) < 0)
return r;
return 0;
}
/*
* Caller passes in a suggested PIN reference.
* See if it's good, and if it isn't, propose something better
*/
static int
incrypto34_select_pin_reference(sc_profile_t *profile, sc_card_t *card,
sc_pkcs15_pin_info_t *pin_info)
{
int preferred, current;
if ((current = pin_info->reference) < 0)
current = INCRYPTO34_PIN_ID_MIN;
if (pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN) {
preferred = 1;
} else {
preferred = current;
/* PINs are even numbered, PUKs are odd */
if (!(preferred & 1))
preferred++;
if (preferred >= 126)
return SC_ERROR_TOO_MANY_OBJECTS;
}
if (current > preferred || preferred > INCRYPTO34_PIN_ID_MAX)
return SC_ERROR_TOO_MANY_OBJECTS;
pin_info->reference = preferred;
return 0;
}
/*
* Store a PIN
*/
static int
incrypto34_create_pin(sc_profile_t *profile, sc_card_t *card, sc_file_t *df,
sc_pkcs15_object_t *pin_obj,
const u8 *pin, size_t pin_len,
const u8 *puk, size_t puk_len)
{
sc_pkcs15_pin_info_t *pin_info = (sc_pkcs15_pin_info_t *) pin_obj->data;
unsigned int puk_id = INCRYPTO34_AC_NEVER;
int r;
if (!pin || !pin_len)
return SC_ERROR_INVALID_ARGUMENTS;
r = sc_select_file(card, &df->path, NULL);
if (r < 0)
return r;
if (puk && puk_len) {
struct sc_pkcs15_pin_info puk_info;
sc_profile_get_pin_info(profile,
SC_PKCS15INIT_USER_PUK, &puk_info);
puk_info.reference = puk_id = pin_info->reference + 1;
r = incrypto34_store_pin(profile, card,
&puk_info, INCRYPTO34_AC_NEVER,
puk, puk_len);
}
if (r >= 0) {
r = incrypto34_store_pin(profile, card,
pin_info, puk_id,
pin, pin_len);
}
return r;
}
/*
* Select a key reference
*/
static int
incrypto34_select_key_reference(sc_profile_t *profile, sc_card_t *card,
sc_pkcs15_prkey_info_t *key_info)
{
struct sc_file *df = profile->df_info->file;
if (key_info->key_reference < INCRYPTO34_KEY_ID_MIN)
key_info->key_reference = INCRYPTO34_KEY_ID_MIN;
if (key_info->key_reference > INCRYPTO34_KEY_ID_MAX)
return SC_ERROR_TOO_MANY_OBJECTS;
key_info->path = df->path;
return 0;
}
/*
* Create a private key object.
* This is a no-op.
*/
static int
incrypto34_create_key(sc_profile_t *profile, sc_card_t *card,
sc_pkcs15_object_t *obj)
{
return 0;
}
/*
* Store a private key object.
*/
static int
incrypto34_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_pkcs15_prkey_info_t *) obj->data;
int algorithm, r;
if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) {
sc_error(card->ctx, "Incrypto34 supports RSA keys only.");
return SC_ERROR_NOT_SUPPORTED;
}
if (incrypto34_key_algorithm(key_info->usage, &algorithm) < 0) {
sc_error(card->ctx, "Incrypto34 does not support keys "
"that can both sign _and_ decrypt.");
return SC_ERROR_NOT_SUPPORTED;
}
r = incrypto34_put_key(profile, card, algorithm, key_info, &key->u.rsa);
return r;
}
/*
* Key generation
*/
static int
incrypto34_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_pkcs15_prkey_info_t *) obj->data;
struct sc_pkcs15_prkey_rsa key_obj;
struct sc_cardctl_incrypto34_genkey_info args;
struct sc_file *temp;
u8 abignum[RSAKEY_MAX_SIZE];
unsigned int keybits;
int algorithm, r, delete_it = 0;
if (obj->type != SC_PKCS15_TYPE_PRKEY_RSA) {
sc_error(card->ctx, "Incrypto34 supports only RSA keys.");
return SC_ERROR_NOT_SUPPORTED;
}
if (incrypto34_key_algorithm(key_info->usage, &algorithm) < 0) {
sc_error(card->ctx, "Incrypto34 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 ((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 = incrypto34_put_key(profile, card, algorithm, key_info, &key_obj);
if (r < 0)
goto out;
memset(&args, 0, sizeof(args));
args.key_id = key_info->key_reference;
args.key_bits = keybits;
args.fid = temp->id;
r = sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_GENERATE_KEY, &args);
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 = incrypto34_extract_pubkey(card, 1, 0x10, &pubkey->u.rsa.modulus);
if (r < 0)
goto out;
r = incrypto34_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_file_free(temp);
if (r < 0) {
if (pubkey->u.rsa.modulus.data)
free (pubkey->u.rsa.modulus.data);
if (pubkey->u.rsa.exponent.data)
free (pubkey->u.rsa.exponent.data);
}
return r;
}
/*
* Store a PIN or PUK
*/
static int
incrypto34_store_pin(sc_profile_t *profile, sc_card_t *card,
sc_pkcs15_pin_info_t *pin_info, int puk_id,
const u8 *pin, size_t pin_len)
{
struct sc_cardctl_incrypto34_obj_info args;
unsigned char buffer[256];
unsigned char pinpadded[16];
struct tlv tlv;
unsigned int attempts, minlen, maxlen;
/* 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;
memcpy(pinpadded, pin, pin_len);
while (pin_len < maxlen)
pinpadded[pin_len++] = profile->pin_pad_char;
pin = pinpadded;
attempts = pin_info->tries_left;
minlen = pin_info->min_length;
tlv_init(&tlv, buffer, sizeof(buffer));
/* object address: class, id */
tlv_next(&tlv, 0x83);
tlv_add(&tlv, 0x00); /* class byte: usage TEST, k=0 */
tlv_add(&tlv, pin_info->reference);
/* parameters */
tlv_next(&tlv, 0x85);
tlv_add(&tlv, 0x02); /* options byte */
tlv_add(&tlv, attempts & 0xf); /* flags byte */
tlv_add(&tlv, INCRYPTO34_ALGO_PIN); /* algorithm = pin-test */
tlv_add(&tlv, attempts & 0xf); /* errcount = attempts */
/* usecount: not documented, but seems to work like this:
* - value of 0xff means pin can be presented any number
* of times
* - anything less: max # of times before BS object is blocked.
*/
tlv_add(&tlv, 0xff);
/* DEK: RFU */
tlv_add(&tlv, 0x00);
/* ARA counter: the number of times the PIN can be used before the he must be verified
again (0 or ff for unlimited usage) */
tlv_add(&tlv, 0x00);
tlv_add(&tlv, minlen); /* minlen */
/* AC conditions */
tlv_next(&tlv, 0x86);
tlv_add(&tlv, 0x00); /* use: always */
tlv_add(&tlv, pin_info->reference); /* change: PIN */
tlv_add(&tlv, puk_id); /* unblock: PUK */
tlv_add(&tlv, 0xFF); /*RFU*/
tlv_add(&tlv, 0xFF); /*RFU*/
tlv_add(&tlv, 0xFF); /*RFU*/
tlv_add(&tlv, 0xFF); /*unused on pins*/
tlv_add(&tlv, 0xFF); /*RFU*/
tlv_add(&tlv, 0xFF); /*RFU*/
tlv_add(&tlv, 0xFF); /*RFU*/
/* data: PIN */
tlv_next(&tlv, 0x8f);
while (pin_len--)
tlv_add(&tlv, *pin++);
args.data = buffer;
args.len = tlv_len(&tlv);
return sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI, &args);
}
/*
* Create an empty security environment
*/
static int
incrypto34_create_sec_env(struct sc_profile *profile, struct sc_card *card,
unsigned int se_id, unsigned int key_id)
{
struct sc_cardctl_incrypto34_obj_info args;
struct tlv tlv;
unsigned char buffer[64];
tlv_init(&tlv, buffer, sizeof(buffer));
tlv_next(&tlv, 0x83);
tlv_add(&tlv, se_id);
tlv_next(&tlv, 0x86);
tlv_add(&tlv, 0);
tlv_add(&tlv, 0);
tlv_next(&tlv, 0x8f);
tlv_add(&tlv, key_id);
tlv_add(&tlv, key_id);
tlv_add(&tlv, key_id);
tlv_add(&tlv, key_id);
tlv_add(&tlv, key_id);
tlv_add(&tlv, key_id);
args.data = buffer;
args.len = tlv_len(&tlv);
return sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_PUT_DATA_SECI, &args);
}
/*
* Determine the key algorithm based on the intended usage
* Note that Incrypto34 does not support keys that can be used
* for signing _and_ decipherment
*/
#define USAGE_ANY_SIGN (SC_PKCS15_PRKEY_USAGE_SIGN|\
SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)
#define USAGE_ANY_DECIPHER (SC_PKCS15_PRKEY_USAGE_DECRYPT|\
SC_PKCS15_PRKEY_USAGE_UNWRAP)
static int
incrypto34_key_algorithm(unsigned int usage, int *algop)
{
int sign = 0, decipher = 0;
if (usage & USAGE_ANY_SIGN) {
*algop = INCRYPTO34_SIGN_RSA;
sign = 1;
}
if (usage & USAGE_ANY_DECIPHER) {
*algop = INCRYPTO34_DECIPHER_RSA;
decipher = 1;
}
return (sign == decipher)? -1 : 0;
}
static int
incrypto34_change_key_data(struct sc_card *card,
unsigned int key_id,
unsigned int num,
const u8 *data, size_t len)
{
struct sc_cardctl_incrypto34_obj_info args;
args.data = (u8 *)data;
args.len = len;
args.key_id = key_id;
args.key_class = num;
return sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_CHANGE_KEY_DATA, &args);
}
/*
* Create a private key object
*/
#define INCRYPTO34_KEY_OPTIONS 0x02
#define INCRYPTO34_KEY_FLAGS 0x00
static int
incrypto34_store_key_component(struct sc_card *card,
int algorithm,
unsigned int key_id, unsigned int pin_id,
unsigned int num,
const u8 *data, size_t len,
int last)
{
int r;
struct sc_cardctl_incrypto34_obj_info args;
struct tlv tlv;
unsigned char buffer[256];
unsigned int n;
/* Initialize the TLV encoder */
tlv_init(&tlv, buffer, sizeof(buffer));
/* Object address */
tlv_next(&tlv, 0x83);
tlv_add(&tlv, 0x20|num); /* PSO, n-th component */
tlv_add(&tlv, key_id);
/* Object parameters */
tlv_next(&tlv, 0x85);
tlv_add(&tlv, INCRYPTO34_KEY_OPTIONS|(last? 0x00 : 0x20));
tlv_add(&tlv, INCRYPTO34_KEY_FLAGS);
tlv_add(&tlv, algorithm);
tlv_add(&tlv, 0x0F); /* Error Counter*/
tlv_add(&tlv, 0xFF); /* use count */
tlv_add(&tlv, 0xFF); /* RFU */
tlv_add(&tlv, 0x00); /* RFU */
tlv_add(&tlv, 0x00); /* RFU */
/* AC bytes */
tlv_next(&tlv, 0x86);
tlv_add(&tlv, pin_id); /* AC USE */
tlv_add(&tlv, pin_id); /* AC CHANGE */
tlv_add(&tlv, 0xFF); /* AC_UNBLOCK */
tlv_add(&tlv, 0xFF); /* RFU */
tlv_add(&tlv, 0xFF); /* RFU */
tlv_add(&tlv, 0xFF); /* RFU */
tlv_add(&tlv, 0); /* AC_GENKEY */
tlv_add(&tlv, 0xFF); /* RFU */
tlv_add(&tlv, 0xFF); /* RFU */
tlv_add(&tlv, 0xFF); /* RFU */
/* SM bytes */
tlv_next(&tlv, 0x8B);
for (n = 0; n < 16; n++)
tlv_add(&tlv, 0xFF);
/* key component */
tlv_next(&tlv, 0x8f);
tlv_add(&tlv, len+1);
tlv_add(&tlv, 0);
while (len--)
tlv_add(&tlv, *data++);
args.data = buffer;
args.len = tlv_len(&tlv);
r = sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI, &args);
return r;
}
static int
incrypto34_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;
key_id = key_info->key_reference;
pin_id = sc_keycache_find_named_pin(&key_info->path, SC_PKCS15INIT_USER_PIN);
if (pin_id < 0)
pin_id = 0;
r = incrypto34_store_key_component(card, algorithm, key_id, pin_id, 0,
key->modulus.data, key->modulus.len, 0);
if (r >= 0)
{
r = incrypto34_store_key_component(card, algorithm, key_id, pin_id, 1,
key->d.data, key->d.len, 1);
}
if (SC_ERROR_FILE_ALREADY_EXISTS == r || r >=0)
{
r = incrypto34_change_key_data(card, 0x80|key_id, 0x20, key->modulus.data, key->modulus.len);
if (r < 0)
return r;
r = incrypto34_change_key_data(card, 0x80|key_id, 0x21, key->d.data, key->d.len);
}
return r;
}
/*
* Extract a key component from the public key file populated by
* GENERATE KEY PAIR
*/
static int
incrypto34_extract_pubkey(sc_card_t *card, int nr, u8 tag,
sc_pkcs15_bignum_t *bn)
{
u8 buf[256];
int r, count;
r = sc_read_record(card, nr, buf, sizeof(buf), SC_RECORD_BY_REC_NR);
if (r < 0)
return r;
count = r - 4;
if (count <= 0 || buf[0] != tag || buf[1] != count + 2
|| buf[2] != count + 1 || buf[3] != 0)
return SC_ERROR_INTERNAL;
bn->len = count;
bn->data = (u8 *) malloc(count);
memcpy(bn->data, buf + 4, count);
return 0;
}
static int incrypto34_init_card(sc_profile_t *profile, sc_card_t *card)
{
return 0;
}
static struct sc_pkcs15init_operations sc_pkcs15init_incrypto34_operations;
static struct sc_pkcs15init_operations sc_pkcs15init_incrypto34_operations = {
incrypto34_erase,
incrypto34_init_card, /* init_card */
incrypto34_create_dir,
NULL, /* create_domain */
incrypto34_select_pin_reference,
incrypto34_create_pin,
incrypto34_select_key_reference,
incrypto34_create_key,
incrypto34_store_key,
incrypto34_generate_key,
NULL, NULL, /* encode private/public key */
NULL, /* finalize_card */
NULL, NULL, NULL, NULL, NULL, /* old style api */
NULL /* delete_object */
};
struct sc_pkcs15init_operations *
sc_pkcs15init_get_incrypto34_ops(void)
{
return &sc_pkcs15init_incrypto34_operations;
}

View File

@ -396,6 +396,7 @@ extern struct sc_pkcs15init_operations *sc_pkcs15init_get_jcop_ops(void);
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_starcos_ops(void);
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_oberthur_ops(void);
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_setcos_ops(void);
extern struct sc_pkcs15init_operations *sc_pkcs15init_get_incrypto34_ops(void);
#ifdef __cplusplus
}

View File

@ -148,6 +148,7 @@ static struct profile_operations {
{ "starcos", (void *) sc_pkcs15init_get_starcos_ops },
{ "oberthur", (void *) sc_pkcs15init_get_oberthur_ops },
{ "setcos", (void *) sc_pkcs15init_get_setcos_ops },
{ "incrypto34", (void *) sc_pkcs15init_get_incrypto34_ops },
{ NULL, NULL },
};