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:
parent
b8981542fb
commit
90d1b0cc9c
|
@ -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 \
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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, ¤t);
|
||||
|
||||
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
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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@
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue