From 52c1b2d700f4d41b3bf04a76f94ce46a06f4898f Mon Sep 17 00:00:00 2001 From: aj Date: Wed, 16 Jun 2004 20:59:59 +0000 Subject: [PATCH] Very basic and untested oberthur driver. Could possibly work, as only non-essential parts stripped (or at least that was the plan). Written by Viktor Tarasov of idealx. All bugs by Andreas Jellinghaus, please don't blame anyone else. git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@1793 c6295689-39f2-0310-b995-f0e70906c6a9 --- src/libopensc/Makefile.am | 1 + src/libopensc/card-oberthur.c | 2318 ++++++++++++++++++++++++++++++ src/libopensc/card-oberthur.h | 70 + src/libopensc/cardctl.h | 45 + src/libopensc/ctx.c | 3 + src/libopensc/opensc.h | 5 +- src/pkcs15init/Makefile.am | 7 +- src/pkcs15init/oberthur.profile | 188 +++ src/pkcs15init/pkcs15-init.h | 1 + src/pkcs15init/pkcs15-lib.c | 23 +- src/pkcs15init/pkcs15-oberthur.c | 718 +++++++++ src/pkcs15init/pkcs15.profile | 11 + 12 files changed, 3375 insertions(+), 15 deletions(-) create mode 100644 src/libopensc/card-oberthur.c create mode 100644 src/libopensc/card-oberthur.h create mode 100644 src/pkcs15init/oberthur.profile create mode 100644 src/pkcs15init/pkcs15-oberthur.c diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index f4c8f9ff..da0f782f 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -27,6 +27,7 @@ libopensc_la_SOURCES = \ card-setcos.c card-miocos.c card-flex.c card-gpk.c \ 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-oberthur.h \ \ pkcs15-openpgp.c pkcs15-infocamere.c libopensc_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ diff --git a/src/libopensc/card-oberthur.c b/src/libopensc/card-oberthur.c new file mode 100644 index 00000000..91d0e3f2 --- /dev/null +++ b/src/libopensc/card-oberthur.c @@ -0,0 +1,2318 @@ +/* + * card-oberthur.c: Support for Oberthur smart cards + * CosmopolIC v5; + * + * Copyright (C) 2001, 2002 Juha Yrjölä + * Copyright (C) 2003 Viktor Tarasov , idealx + * + * 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 + * + * best view with tabstop=4 + */ + +#include "internal.h" +#include "cardctl.h" +#include "pkcs15.h" +#ifdef HAVE_OPENSSL + +#include +#include +#include + +#include + +#include "log.h" +#include "pkcs15.h" + +#include "card-oberthur.h" + +NTLV_t oberthur_atrs[] = { + { "Oberthur 32k", ATR_OBERTHUR_32K, 20, + "\x3B\x7F\x18\x00\x00\x00\x31\xC0\x73\x9E\x01\x0B\x64\x52\xD9\x04\x00\x82\x90\x00" + }, + { "Oberthur 32k BIO", ATR_OBERTHUR_32K_BIO, 20, + "\x3B\x7F\x18\x00\x00\x00\x31\xC0\x73\x9E\x01\x0B\x64\x52\xD9\x05\x00\x82\x90\x00" + }, + { "Oberthur 64k v4/2.1.1", ATR_OBERTHUR_64K, 18, + "\x3B\x7D\x18\x00\x00\x00\x31\x80\x71\x8E\x64\x77\xE3\x01\x00\x82\x90\x00" + }, + { "Oberthur 64k v5", ATR_OBERTHUR_64K, 18, + "\x3B\x7D\x11\x00\x00\x00\x31\x80\x71\x8E\x64\x77\xE3\x01\x00\x82\x90\x00" + }, + { "Oberthur 64k v5/2.2.0", ATR_OBERTHUR_64K, 18, + "\x3B\x7D\x11\x00\x00\x00\x31\x80\x71\x8E\x64\x77\xE3\x02\x00\x82\x90\x00" + }, + { NULL, 0, 0, NULL } +}; + + +NTLV_t oberthur_aids[] = { +/* + { "AuthentIC v2", AID_OBERTHUR_V2, 14, + "\xA0\x00\x00\x00\x77\x58\x35\x30\x39\x23\x56\x32\x2E\x30" + }, + { "AuthentIC v4", AID_OBERTHUR_V4, 16, + "\xA0\x00\x00\x00\x77\x01\x03\x03\x00\x20\x03\xF1\x00\x00\x00\x02" + }, +*/ + { "AuthentIC v5", AID_OBERTHUR_V5, 16, + "\xA0\x00\x00\x00\x77\x01\x03\x03\x00\x00\x00\xF1\x00\x00\x00\x02" + }, + { NULL, 0, 0, NULL } +}; + + +static unsigned char rsa_der[PUBKEY_2048_ASN1_SIZE]; +static int rsa_der_len = 0; + +static struct sc_file last_selected_file; +static struct sc_card_operations auth_ops; +static struct sc_card_operations *iso_ops; +static struct sc_card_driver auth_drv = { + "Oberthur AuthentIC.v2/CosmopolIC.v4", + "oberthur", + &auth_ops +}; + + +static int auth_get_pin_reference (struct sc_card *card, + int type, int reference, int cmd, int *out_ref); +static int auth_read_component(sc_card_t *card, + enum SC_CARDCTL_OBERTHUR_KEY_TYPE type, int num, + unsigned char *out, size_t outlen); +static int auth_verify(struct sc_card *card, unsigned int type, + int ref, const u8 *data, size_t data_len, int *tries_left); +static int auth_create_reference_data (struct sc_card *card, + struct sc_cardctl_oberthur_createpin_info *args); + + +static int +auth_finish(struct sc_card *card) +{ + free(card->drv_data); + return 0; +} + + +static int +auth_select_aid(struct sc_card *card) +{ + struct sc_apdu apdu; + unsigned char apdu_resp[SC_MAX_APDU_BUFFER_SIZE]; + struct auth_private_data *data = card->drv_data; + int rv, ii; + unsigned char cm[7] = {0xA0,0x00,0x00,0x00,0x03,0x00,0x00}; + + // Select Card Manager (to deselect previously selected application) + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x00); + apdu.lc = sizeof(cm); + apdu.le = sizeof(cm)+4; + apdu.data = cm; + apdu.datalen = sizeof(cm); + apdu.resplen = sizeof(apdu.resp); + apdu.resp = apdu_resp; + + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + // Get smart card serial number + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xCA, 0x9F, 0x7F); + apdu.cla = 0x80; + apdu.le = 0x2D; + apdu.resplen = 0x30; + apdu.resp = apdu_resp; + sc_transmit_apdu(card, &apdu); + if (apdu.sw1==0x90) { + sc_debug(card->ctx, "serial number %li\n", + *(apdu.resp+15)*0x1000000 + *(apdu.resp+16)*0x10000 + + *(apdu.resp+17)*0x100 + *(apdu.resp+18)); + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x00); + apdu.resp = apdu_resp; + + // Try to select known AID + for (ii = 0; oberthur_aids[ii].value != NULL; ii++) { + size_t len = oberthur_aids[ii].len; + unsigned char *ptr; + + ptr = oberthur_aids[ii].value; + apdu.lc = len; + apdu.le = len + 4; + apdu.data = oberthur_aids[ii].value; + apdu.datalen = len; + apdu.resplen = SC_MAX_AID_SIZE + 8; + + rv = sc_transmit_apdu(card, &apdu); + if (rv < 0) + continue; + if (apdu.sw1!=0x90 || apdu.sw2!=0x00) + continue; + if (!memcmp(oberthur_aids[ii].value, apdu.resp+4, len)) { + memcpy(data->aid.value, oberthur_aids[ii].value, len); + data->aid.len = len; + data->aid.tag = oberthur_aids[ii].tag; + card->name = oberthur_aids[ii].name; + break; + } + } + + return oberthur_aids[ii].value == NULL ? -1 : 0; +} + + +static int +auth_match_card(struct sc_card *card) +{ + int ii; + + for (ii = 0; oberthur_atrs[ii].value != NULL; ii++) { + if (oberthur_atrs[ii].len != card->atr_len) + continue; + else if (!memcmp(card->atr, oberthur_atrs[ii].value, oberthur_atrs[ii].len)) + return 1; + } + + return 0; +} + + +static int +auth_init(struct sc_card *card) +{ + unsigned long flags; + struct auth_private_data *data; + + data = (struct auth_private_data *) malloc(sizeof(struct auth_private_data)); + if (!data) + return SC_ERROR_OUT_OF_MEMORY; + else + memset(data, 0, sizeof(struct auth_private_data)); + + card->cla = 0x00; + card->drv_data = data; + + /* State that we have an RNG */ + card->caps |= SC_CARD_CAP_RNG; + + if (auth_select_aid(card)) { + sc_error(card->ctx, "Failed to initialize %s\n", card->name); + return SC_ERROR_INVALID_CARD; + } + + flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_PAD_ISO9796; + flags |= SC_ALGORITHM_RSA_HASH_NONE; + flags |= SC_ALGORITHM_ONBOARD_KEY_GEN; + + _sc_card_add_rsa_alg(card, 512, flags, 0); + _sc_card_add_rsa_alg(card, 1024, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); +#if 0 + flags = SC_ALGORITHM_SKEY_CBC | SC_ALGORITHM_SKEY_ECB; + memset(&info, 0, sizeof(info)); + info.algorithm = SC_ALGORITHM_DES; + info.flags = flags; + info.key_length = 64; + _sc_card_add_algorithm(card, &info); + + flags = SC_ALGORITHM_SKEY_CBC | SC_ALGORITHM_SKEY_ECB; + info.algorithm = SC_ALGORITHM_3DES; + info.flags = flags; + info.key_length = 192; + _sc_card_add_algorithm(card, &info); +#endif + return 0; +} + + +static void +add_acl_entry(struct sc_card *card, struct sc_file *file, unsigned int op, + u8 acl_byte) +{ + struct auth_private_data *data = card->drv_data; + + switch (data->aid.tag) { + case AID_OBERTHUR_V5 : + switch (acl_byte) { + case 0x00: + sc_file_add_acl_entry(file, op, SC_AC_NONE, SC_AC_KEY_REF_NONE); + break; + case 0x21: + sc_file_add_acl_entry(file, op, SC_AC_CHV, 1); + break; + case 0x24: + case 0x0F: + sc_file_add_acl_entry(file, op, SC_AC_CHV, 2); + break; + case 0x25: + sc_file_add_acl_entry(file, op, SC_AC_CHV, 3); + break; + case 0xFF: + sc_file_add_acl_entry(file, op, SC_AC_NEVER, SC_AC_KEY_REF_NONE); + break; + default: + sc_file_add_acl_entry(file, op, SC_AC_UNKNOWN, SC_AC_KEY_REF_NONE); + break; + } + break; + default: + break; + + } +} + + +int +tlv_get(unsigned char *msg, unsigned char tag, unsigned char *ret, int *ret_len) +{ + int len = *(msg+1); + int cur = 2; + + if (*msg != 0x6F || len > 0x1A) + return SC_ERROR_INCORRECT_PARAMETERS; + + while (cur < len) { + if (*(msg+cur)==tag) { + int ii, ln = *(msg+cur+1); + + if (ln > *ret_len) + return SC_ERROR_WRONG_LENGTH; + + for (ii=0; iiid = attr[0]*0x100 + attr[1]; + + attr_len = sizeof(attr); + if (tlv_get(buf, type==0x01 ? 0x80 : 0x85, attr, &attr_len)) + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + if (attr_len<2 && type != 0x04) + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + + switch (type) { + case 0x01: + file->type = SC_FILE_TYPE_WORKING_EF; + file->ef_structure = SC_FILE_EF_TRANSPARENT; + file->size = attr[0]*0x100 + attr[1]; + break; + case 0x04: + file->type = SC_FILE_TYPE_WORKING_EF; + file->ef_structure = SC_FILE_EF_LINEAR_VARIABLE; + file->size = attr[0]; + attr_len = sizeof(attr); + if (tlv_get(buf, 0x82, attr, &attr_len)) + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + if (attr_len!=5) + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + file->record_length = attr[2]*0x100+attr[3]; + file->record_count = attr[4]; + break; + case 0x11: + file->type = SC_FILE_TYPE_INTERNAL_EF; + file->ef_structure = SC_CARDCTL_OBERTHUR_KEY_DES; + file->size = attr[0]*0x100 + attr[1]; + file->size /= 8; + break; + case 0x12: + file->type = SC_FILE_TYPE_INTERNAL_EF; + file->ef_structure = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; + + file->size = attr[0]*0x100 + attr[1]; + if (file->size==512) + file->size = PUBKEY_512_ASN1_SIZE; + else if (file->size==1024) + file->size = PUBKEY_1024_ASN1_SIZE; + else if (file->size==2048) + file->size = PUBKEY_2048_ASN1_SIZE; + else { + sc_error(card->ctx, "Not supported public key size: %i\n", file->size); + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + } + break; + case 0x14: + file->type = SC_FILE_TYPE_INTERNAL_EF; + file->ef_structure = SC_CARDCTL_OBERTHUR_KEY_RSA_CRT; + file->size = attr[0]*0x100 + attr[1]; + break; + case 0x38: + file->type = SC_FILE_TYPE_DF; + file->size = attr[0]; + sc_file_set_type_attr(file,attr,attr_len); + break; + default: + sc_error(card->ctx, "invalid file type: 0x%02X\n", type); + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + } + + attr_len = sizeof(attr); + if (tlv_get(buf, 0x86, attr, &attr_len)) + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + if (attr_len<8) + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + + if (file->type == SC_FILE_TYPE_DF) { + add_acl_entry(card, file, SC_AC_OP_CREATE, attr[0]); + add_acl_entry(card, file, SC_AC_OP_CRYPTO, attr[1]); + add_acl_entry(card, file, SC_AC_OP_LIST_FILES, attr[2]); + add_acl_entry(card, file, SC_AC_OP_DELETE, attr[3]); +#if 0 + add_acl_entry(card, file, SC_AC_OP_CHANGE_REFERENCE, attr[4]); + add_acl_entry(card, file, SC_AC_OP_SET_REFERENCE, attr[5]); + add_acl_entry(card, file, SC_AC_OP_RESET_COUNTER, attr[6]); +#endif + } + else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { /* EF */ + switch (file->ef_structure) { + case SC_CARDCTL_OBERTHUR_KEY_DES: + add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[0]); +#if 0 + add_acl_entry(card, file, SC_AC_OP_DECRYPT, attr[1]); + add_acl_entry(card, file, SC_AC_OP_ENCRYPT, attr[2]); + add_acl_entry(card, file, SC_AC_OP_CHECKSUM, attr[3]); + add_acl_entry(card, file, SC_AC_OP_VERIFY, attr[4]); +#else + add_acl_entry(card, file, SC_AC_OP_READ, attr[1]); +#endif + break; + case SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC: + add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[0]); +#if 0 + add_acl_entry(card, file, SC_AC_OP_ENCRYPT, attr[2]); + add_acl_entry(card, file, SC_AC_OP_VERIFY, attr[4]); +#else + add_acl_entry(card, file, SC_AC_OP_READ, attr[2]); +#endif + break; + case SC_CARDCTL_OBERTHUR_KEY_RSA_CRT: + add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[0]); +#if 0 + add_acl_entry(card, file, SC_AC_OP_DECRYPT, attr[1]); + add_acl_entry(card, file, SC_AC_OP_SIGN, attr[3]); +#else + add_acl_entry(card, file, SC_AC_OP_READ, attr[1]); +#endif + break; + } + } + else { + switch (file->ef_structure) { + case SC_FILE_EF_TRANSPARENT: + add_acl_entry(card, file, SC_AC_OP_WRITE, attr[0]); + add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[1]); + add_acl_entry(card, file, SC_AC_OP_READ, attr[2]); + add_acl_entry(card, file, SC_AC_OP_ERASE, attr[3]); + break; + case SC_FILE_EF_LINEAR_VARIABLE: + add_acl_entry(card, file, SC_AC_OP_WRITE, attr[0]); + add_acl_entry(card, file, SC_AC_OP_UPDATE, attr[1]); + add_acl_entry(card, file, SC_AC_OP_READ, attr[2]); + add_acl_entry(card, file, SC_AC_OP_ERASE, attr[3]); + break; + } + } + + file->status = SC_FILE_STATUS_ACTIVATED; + file->magic = SC_FILE_MAGIC; + return 0; +} + + +static int +check_path(struct sc_card *card, const u8 **pathptr, size_t *pathlen, + int need_info) +{ + const u8 *curptr = card->cache.current_path.value; + const u8 *ptr = *pathptr; + size_t curlen = card->cache.current_path.len; + size_t len = *pathlen; + + if (curlen < 2 || len < 2) + return 0; + if (memcmp(ptr, "\x3F\x00", 2) != 0) { + /* Skip the MF id */ + curptr += 2; + curlen -= 2; + } + if (len == curlen && memcmp(ptr, curptr, len) == 0) { + if (need_info) + return 0; + *pathptr = ptr + len; + *pathlen = 0; + return 1; + } + if (curlen < len && memcmp(ptr, curptr, curlen) == 0) { + *pathptr = ptr + curlen; + *pathlen = len - curlen; + return 1; + } + + return 0; +} + + +void +auth_cache_path(struct sc_card *card, const struct sc_path *path) +{ + struct sc_path *curpath = &card->cache.current_path; + + switch (path->type) { + case SC_PATH_TYPE_FILE_ID: + if (path->value[0] == 0x3F && path->value[1] == 0x00) + sc_format_path("3F00", curpath); + else { + if (curpath->len + 2 > SC_MAX_PATH_SIZE) { + curpath->len = 0; + return; + } + memcpy(curpath->value + curpath->len, path->value, 2); + curpath->len += 2; + } + break; + case SC_PATH_TYPE_PATH: + curpath->len = 0; + if (path->value[0] != 0x3F || path->value[1] != 0) + sc_format_path("3F00", curpath); + if (curpath->len + path->len > SC_MAX_PATH_SIZE) { + curpath->len = 0; + return; + } + memcpy(curpath->value + curpath->len, path->value, path->len); + curpath->len += path->len; + break; + case SC_PATH_TYPE_DF_NAME: + /* All bets are off */ + curpath->len = 0; + break; + } +} + + +static int +select_parent(struct sc_card *card, struct sc_file **file_out) +{ + int rv; + struct sc_apdu apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + struct sc_file *file; + struct sc_path *cache_path = &card->cache.current_path; + struct auth_private_data *prv = card->drv_data; + + last_selected_file.magic = 0; + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x03, 0); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 0x18; + + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + + if (apdu.resplen < 14) { + sc_error(card->ctx, "invalid response length\n"); + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + } + else if (apdu.resp[0] != 0x6F) { + sc_error(card->ctx, "unsupported: card returned FCI\n"); + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + } + + if (cache_path->len > 2) + cache_path->len -= 2; + + file = sc_file_new(); + if (prv->aid.tag == AID_OBERTHUR_V5) + rv = decode_file_structure_V5(card, apdu.resp, apdu.resplen, file); + else { + sc_file_free(file); + return SC_ERROR_INVALID_CARD; + } + + if (rv) { + sc_file_free(file); + return rv; + } + + memcpy(&last_selected_file, file, sizeof(struct sc_file)); + + if (file_out) + *file_out = file; + else + sc_file_free(file); + + return 0; +} + + +static int +select_mf(struct sc_card *card, struct sc_file **file_out) +{ + int ii,rv; + struct sc_file *file = NULL; + struct sc_path *cache_path = &card->cache.current_path; + + last_selected_file.magic = 0; + for(ii=0;;ii++) { + rv = select_parent(card, &file); + SC_TEST_RET(card->ctx, rv, "Select parent failed"); + + if (file->id==0x3F00) + break; + else + sc_file_free(file); + + if (ii>5) + return SC_ERROR_CARD_CMD_FAILED; + } + + memcpy(cache_path->value, "\x3F\x00", 2); + cache_path->len = 2; + + memcpy(&last_selected_file, file, sizeof(struct sc_file)); + if (file && file_out) + *file_out = file; + else if (file) + sc_file_free(file); + + return 0; +} + + +static int +select_file_id(struct sc_card *card, const u8 *buf, size_t buflen, + u8 p1, struct sc_file **file_out) +{ + int rv; + struct sc_apdu apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + struct sc_file *file; + struct auth_private_data *prv = card->drv_data; + + last_selected_file.magic = 0; + if (buflen==2 && memcmp(buf,"\x3F\x00",2)==0) { + rv = select_mf(card,file_out); + SC_TEST_RET(card->ctx, rv, "Select MF failed"); + return rv; + } + + if (!memcmp(buf,"\x00\x00",2) || !memcmp(buf,"\xFF\xFF",2) || + !memcmp(buf,"\x3F\xFF",2)) + return SC_ERROR_INCORRECT_PARAMETERS; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, p1, 0); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.datalen = buflen; + apdu.data = buf; + apdu.lc = buflen; + + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + + if (apdu.resplen < 14) + return SC_ERROR_UNKNOWN_DATA_RECEIVED; + + if (apdu.resp[0] != 0x6F) { + sc_error(card->ctx, "unsupported: card returned FCI\n"); + return SC_ERROR_UNKNOWN_DATA_RECEIVED; /* FIXME */ + } + + file = sc_file_new(); + if (prv->aid.tag == AID_OBERTHUR_V5) + rv = decode_file_structure_V5(card, apdu.resp, apdu.resplen, file); + else { + sc_file_free(file); + return SC_ERROR_INVALID_CARD; + } + + if (rv) { + sc_file_free(file); + return rv; + } + + memcpy(&last_selected_file, file, sizeof(struct sc_file)); + + if (file->type == SC_FILE_TYPE_DF) { + struct sc_path *cache_path = &card->cache.current_path; + int len = cache_path->len; + + if (len < sizeof(cache_path->value)) { + memcpy(&cache_path->value[len], buf, 2); + cache_path->len += 2; + } + } + + sc_debug(card->ctx, "selected %04X\n",file->id); + if (file_out) + *file_out = file; + else + sc_file_free(file); + + return rv; +} + + +static int +auth_select_file(struct sc_card *card, const struct sc_path *path, + struct sc_file **file_out) +{ + int rv; + const u8 *pathptr = path->value; + size_t pathlen = path->len; + int locked = 0, magic_done; + u8 p1 = 0; + + sc_debug(card->ctx, "path; type=%d, path=%s\n", + path->type, sc_print_path(path)); + sc_debug(card->ctx, "cache; type=%d, path=%s\n", + card->cache.current_path.type, sc_print_path(&card->cache.current_path)); + + switch (path->type) { + case SC_PATH_TYPE_PATH: + if ((pathlen & 1) != 0) /* not divisible by 2 */ + return SC_ERROR_INVALID_ARGUMENTS; + + magic_done = check_path(card, &pathptr, &pathlen, file_out != NULL); + if (pathlen == 0) + return 0; + + if (pathlen != 2 || memcmp(pathptr, "\x3F\x00", 2) != 0) { + locked = 1; + rv = sc_lock(card); + SC_TEST_RET(card->ctx, rv, "sc_lock() failed"); + if (!magic_done && memcmp(pathptr, "\x3F\x00", 2) != 0) { + rv = select_file_id(card, (const u8 *) "\x3F\x00", 2, 0, NULL); + if (rv) + sc_unlock(card); + SC_TEST_RET(card->ctx, rv, "Unable to select Master File (MF)"); + } + while (pathlen > 2) { + rv = select_file_id(card, pathptr, 2, 0, NULL); + if (rv) + sc_unlock(card); + SC_TEST_RET(card->ctx, rv, "Unable to select DF"); + pathptr += 2; + pathlen -= 2; + } + } + break; + case SC_PATH_TYPE_DF_NAME: + p1 = 0x01; + break; + case SC_PATH_TYPE_FILE_ID: + p1 = 0x02; + if (pathlen != 2) + return SC_ERROR_INVALID_ARGUMENTS; + break; + } + + rv = select_file_id(card, pathptr, pathlen, p1, file_out); + + if (locked) + sc_unlock(card); + +// if (!rv) +// auth_cache_path(card, path); + + sc_debug(card->ctx, "return %i\n",rv); + return rv; +} + + +static int +auth_list_files(struct sc_card *card, u8 *buf, size_t buflen) +{ + struct sc_apdu apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; + int rv; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x34, 0, 0); + apdu.cla = 0x80; + apdu.le = 0x40; + apdu.resplen = sizeof(rbuf); + apdu.resp = rbuf; + + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + + buflen = buflen < apdu.resplen ? buflen : apdu.resplen; + memcpy(buf, rbuf, buflen); + + return buflen; +} + + +static int +auth_delete_file(struct sc_card *card, const struct sc_path *path) +{ + int rv; + u8 sbuf[2]; + struct sc_apdu apdu; + + sc_debug(card->ctx, "path; type=%d, path=%s\n", path->type, sc_print_path(path)); + SC_FUNC_CALLED(card->ctx, 1); + if (path->len < 2) { + sc_error(card->ctx, "Invalid path length\n"); + SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS); + } + + if (path->len > 2) { + struct sc_path parent = *path; + + parent.len -= 2; + parent.type = SC_PATH_TYPE_PATH; + rv = auth_select_file(card, &parent, NULL); + SC_TEST_RET(card->ctx, rv, "select parent failed "); + } + + sbuf[0] = path->value[path->len - 2]; + sbuf[1] = path->value[path->len - 1]; + + if (memcmp(sbuf,"\x00\x00",2)==0 || (memcmp(sbuf,"\xFF\xFF",2)==0) || + memcmp(sbuf,"\x3F\xFF",2)==0) + return SC_ERROR_INCORRECT_PARAMETERS; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE4, 0x02, 0x00); + apdu.lc = 2; + apdu.datalen = 2; + apdu.data = sbuf; + + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + if (apdu.sw1==0x6A && apdu.sw2==0x82) { + /* Clean the DF contents.*/ +// struct sc_file *file; + u8 lbuf[SC_MAX_APDU_BUFFER_SIZE]; + int ii, len; + +// if (!(file = sc_file_new())) +// SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + +// rv = select_file_id(card, sbuf, 2, 0x01, &file); + rv = select_file_id(card, sbuf, 2, 0x01, NULL); + SC_TEST_RET(card->ctx, rv, "select DF failed"); + + len = auth_list_files(card, lbuf, sizeof(lbuf)); + SC_TEST_RET(card->ctx, len, "list DF failed"); + + for (ii=0; iictx, rv, "delete failed"); + } +// rv = select_parent(card, &file); + rv = select_parent(card, NULL); + SC_TEST_RET(card->ctx, rv, "select parent DF failed"); + + apdu.p1 = 1; + rv = sc_transmit_apdu(card, &apdu); + } + + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card return error"); + return rv; +} + + +static int +acl_to_ac_byte(struct sc_card *card, const struct sc_acl_entry *e) +{ + struct auth_private_data *data = card->drv_data; + + if (e == NULL) + return -1; + switch (data->aid.tag) { + case AID_OBERTHUR_V5 : + switch (e->method) { + case SC_AC_NONE: + return 0x00; + + case SC_AC_CHV: + if (e->key_ref == 1) + return 0x21; + else if (e->key_ref == 2) + return 0x24; + else if (e->key_ref == 3) + return 0x25; + else + return -1; + + case SC_AC_NEVER: + return 0xff; + } + break; + } + + sc_error(card->ctx, "unknown method or aid %i; tag %X\n",e->method, data->aid.tag); + return SC_ERROR_INCORRECT_PARAMETERS; +} + + +static int +encode_file_structure_V5(struct sc_card *card, const struct sc_file *file, + u8 *buf, size_t *buflen) +{ + u8 *p = buf; + int rv=0, ii, size; + unsigned char ops[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + sc_debug(card->ctx, ": id %04X; size %i; type %i/%i\n", + file->id, file->size, file->type, file->ef_structure); + if (*buflen < 0x18) { + sc_error(card->ctx, "Insifficient buffer size.\n"); + return SC_ERROR_INCORRECT_PARAMETERS; + } + + p[0] = 0x62, p[1] = 0x16; + p[2] = 0x82, p[3] = 0x02; + + rv = 0; + if (file->type == SC_FILE_TYPE_DF) { + p[4] = 0x38; + p[5] = 0x00; + } + else if (file->type == SC_FILE_TYPE_WORKING_EF) { + switch (file->ef_structure) { + case SC_FILE_EF_TRANSPARENT: + p[4] = 0x01; + p[5] = 0x01; + break; + case SC_FILE_EF_LINEAR_VARIABLE: + p[4] = 0x04; + p[5] = 0x01; + break; + default: + rv = -1; + break; + } + } + else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { + switch (file->ef_structure) { + case SC_CARDCTL_OBERTHUR_KEY_DES: + p[4] = 0x11; + p[5] = 0x00; + break; + case SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC: + p[4] = 0x12; + p[5] = 0x00; + break; + case SC_CARDCTL_OBERTHUR_KEY_RSA_CRT: + p[4] = 0x14; + p[5] = 0x00; + break; + default: + rv = -1; + break; + } + } + else + rv = -1; + + if (rv) { + sc_error(card->ctx, "Invalid EF structure %i/%i\n", + file->type, file->ef_structure); + return -1; + } + + p[6] = 0x83; + p[7] = 0x02; + p[8] = file->id >> 8; + p[9] = file->id & 0xFF; + + p[10] = 0x85; + p[11] = 0x02; + + size = file->size; + + if (file->type == SC_FILE_TYPE_DF) { + size &= 0xFF; + } + else if (file->type == SC_FILE_TYPE_INTERNAL_EF && + file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + /* + * Legal sizes corresponds to 512/1024/2048 key size + * and 3 bytes exponent. + */ + sc_debug(card->ctx, "ef %s\n","SC_FILE_EF_RSA_PUBLIC"); + if (file->size == PUBKEY_512_ASN1_SIZE || file->size == 512) + size = 512; + else if (file->size == PUBKEY_1024_ASN1_SIZE || file->size == 1024) + size = 1024; + else if (file->size == PUBKEY_2048_ASN1_SIZE || file->size == 2048) + size = 2048; + else { + sc_error(card->ctx, "incorrect RSA size %X\n", file->size); + return SC_ERROR_INCORRECT_PARAMETERS; + } + } + else if (file->type == SC_FILE_TYPE_INTERNAL_EF && + file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_DES) { + if (file->size == 8 || file->size == 64) + size = 64; + else if (file->size == 16 || file->size == 128) + size = 128; + else if (file->size == 24 || file->size == 192) + size = 192; + else { + sc_error(card->ctx, "incorrect DES size %X\n", file->size); + return SC_ERROR_INCORRECT_PARAMETERS; + } + } + + p[12] = (size >> 8) & 0xFF; + p[13] = size & 0xFF; + + p[14] = 0x86; + p[15] = 0x08; + + if (file->type == SC_FILE_TYPE_DF) { + ops[0] = SC_AC_OP_CREATE; + ops[1] = SC_AC_OP_CRYPTO; + ops[2] = SC_AC_OP_LIST_FILES; + ops[3] = SC_AC_OP_DELETE; + ops[4] = SC_AC_OP_LIST_FILES; // SC_AC_OP_SET_REFERENCE + ops[5] = SC_AC_OP_LIST_FILES; // SC_AC_OP_CHANGE_REFERENCE + ops[6] = SC_AC_OP_LIST_FILES; // SC_AC_OP_RESET_COUNTER + } + else if (file->type == SC_FILE_TYPE_WORKING_EF) { + if (file->ef_structure == SC_FILE_EF_TRANSPARENT) { + sc_debug(card->ctx, "SC_FILE_EF_TRANSPARENT\n"); + ops[0] = SC_AC_OP_WRITE; + ops[1] = SC_AC_OP_UPDATE; + ops[2] = SC_AC_OP_READ; + ops[3] = SC_AC_OP_ERASE; + } + else if (file->ef_structure == SC_FILE_EF_LINEAR_VARIABLE) { + sc_debug(card->ctx, "SC_FILE_EF_LINEAR_VARIABLE\n"); + ops[0] = SC_AC_OP_WRITE; + ops[1] = SC_AC_OP_UPDATE; + ops[2] = SC_AC_OP_READ; + ops[3] = SC_AC_OP_ERASE; + } + } + else if (file->type == SC_FILE_TYPE_INTERNAL_EF) { + if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_DES) { + sc_debug(card->ctx, "EF_DES\n"); + ops[0] = SC_AC_OP_UPDATE; + ops[1] = SC_AC_OP_READ; // SC_AC_OP_DECRYPT + ops[2] = SC_AC_OP_READ; // SC_AC_OP_ENCRYPT + ops[3] = SC_AC_OP_READ; // SC_AC_OP_CHECKSUM + ops[4] = SC_AC_OP_READ; // SC_AC_OP_CHECKSUM + } + else if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + sc_debug(card->ctx, "EF_RSA_PUBLIC\n"); + ops[0] = SC_AC_OP_UPDATE; + ops[2] = SC_AC_OP_READ; // SC_AC_OP_ENCRYPT + ops[4] = SC_AC_OP_READ; // SC_AC_OP_SIGN + } + else if (file->ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_CRT) { + sc_debug(card->ctx, "EF_RSA_PRIVATE\n"); + ops[0] = SC_AC_OP_UPDATE; + ops[1] = SC_AC_OP_READ; // SC_AC_OP_ENCRYPT + ops[3] = SC_AC_OP_READ; // SC_AC_OP_SIGN + } + } + + for (ii = 0; ii < sizeof(ops); ii++) { + const struct sc_acl_entry *entry; + + p[16+ii] = 0xFF; + if (ops[ii]==0xFF) + continue; + entry = sc_file_get_acl_entry(file, ops[ii]); + rv = acl_to_ac_byte(card,entry); + SC_TEST_RET(card->ctx, rv, "Invalid ACL value"); + p[16+ii] = rv; + } + + *buflen = 0x18; + + return 0; +} + + +static int +auth_create_file(struct sc_card *card, struct sc_file *file) +{ + u8 sbuf[0x18]; + size_t sendlen = sizeof(sbuf); + int rv, rec_nr; + struct sc_apdu apdu; + struct sc_path path; + struct auth_private_data *prv = card->drv_data; + + sc_debug(card->ctx, " create path=%s\n", sc_print_path(&file->path)); + sc_debug(card->ctx,"id %04X; size %i; type %i; ef %i\n", + file->id, file->size, file->type, file->ef_structure); + if (file->id==0x0000 || file->id==0xFFFF || file->id==0x3FFF) + return SC_ERROR_INVALID_ARGUMENTS; + + sc_debug(card->ctx, " cache path=%s\n", + sc_print_path(&card->cache.current_path)); + + if (file->path.len) { + memcpy(&path, &file->path, sizeof(path)); + if (path.len>2) + path.len -= 2; + + if (auth_select_file(card, &path, NULL)) { + sc_error(card->ctx, "Cannot select parent DF.\n"); + return SC_ERROR_INVALID_ARGUMENTS; + } + } + + if (prv->aid.tag == AID_OBERTHUR_V5) + rv = encode_file_structure_V5(card, file, sbuf, &sendlen); + else + return SC_ERROR_INVALID_CARD; + + if (rv) { + sc_error(card->ctx, "File structure encoding failed.\n"); + return SC_ERROR_INVALID_ARGUMENTS; + } + + if (file->type != SC_FILE_TYPE_DF && file->ef_structure != SC_FILE_EF_TRANSPARENT) + rec_nr = file->record_count; + else + rec_nr = 0; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, rec_nr); + apdu.data = sbuf; + apdu.datalen = sendlen; + apdu.lc = sendlen; + + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + + /* select created DF. */ + if (file->type == SC_FILE_TYPE_DF) { + u8 file_id[2] = {file->id >> 8, file->id & 0xFF}; + + if (select_file_id(card, file_id, 2, 0x01, NULL)) + return SC_ERROR_CARD_CMD_FAILED; + + if (card->cache_valid) { + u8 file_id[2]; + + file_id[0] = file->id >> 8; + file_id[1] = file->id & 0xFF; + if (card->cache.current_path.len != 0) + sc_append_path_id(&card->cache.current_path, file_id, 2); + } + } + + return 0; +} + + +static int +auth_set_security_env(struct sc_card *card, + const struct sc_security_env *env, int se_num) +{ + auth_senv_t *senv = &((struct auth_private_data *) card->drv_data)->senv; + long unsigned pads = env->algorithm_flags & SC_ALGORITHM_RSA_PADS; + long unsigned supported_pads = + SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_PAD_ISO9796; + struct sc_file *key_file = NULL; + struct sc_apdu apdu; + u8 rsa_sbuf[7] = {0x80, 0x01, 0xFF, 0x81, 0x02, 0xFF, 0xFF}; + int des_buf_len; + u8 des_sbuf[17] = {0x80, 0x01, 0x01, 0x81, 0x02, 0xFF, 0xFF, + 0x87, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF}; + struct auth_private_data *prv = card->drv_data; + int rv; + + sc_debug(card->ctx, "op %i\n", env->operation); + + memset(senv,0,sizeof(auth_senv_t)); + + rv = auth_select_file(card, &env->file_ref, &key_file); + if (rv) + return rv; + + switch (env->algorithm) { + case SC_ALGORITHM_DES: + case SC_ALGORITHM_3DES: + sc_debug(card->ctx, "algo SC_ALGORITHM_xDES: ref %X, flags %X\n", + env->algorithm_ref, env->flags); + if (key_file->ef_structure != SC_CARDCTL_OBERTHUR_KEY_DES || + key_file->type != SC_FILE_TYPE_INTERNAL_EF) + return SC_ERROR_INVALID_ARGUMENTS; + + des_buf_len = 3; + if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT) { + des_sbuf[5] = (key_file->id>>8) & 0xFF; + des_sbuf[6] = key_file->id & 0xFF; + des_buf_len += 4; + } + + if (env->operation == SC_SEC_OPERATION_DECIPHER) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB8); + apdu.lc = des_buf_len; + apdu.data = des_sbuf; + apdu.datalen = des_buf_len; + } + else { + sc_error(card->ctx, "Invalid crypto operation: %X\n", env->operation); + return SC_ERROR_NOT_SUPPORTED; + } + + break; + case SC_ALGORITHM_RSA: + sc_debug(card->ctx, "algo SC_ALGORITHM_RSA\n"); + if (env->algorithm_flags & SC_ALGORITHM_RSA_HASHES) { + sc_error(card->ctx, "Not support for hashes.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + + if (pads & (~supported_pads)) { + sc_error(card->ctx, "No support for this PAD: %X\n",pads); + return SC_ERROR_NOT_SUPPORTED; + } + + if (key_file->type != SC_FILE_TYPE_INTERNAL_EF || + key_file->ef_structure != SC_CARDCTL_OBERTHUR_KEY_RSA_CRT) + return SC_ERROR_INVALID_ARGUMENTS; + + rsa_sbuf[5] = (key_file->id>>8) & 0xFF; + rsa_sbuf[6] = key_file->id & 0xFF; + if (env->operation == SC_SEC_OPERATION_SIGN) { + rsa_sbuf[2] = prv->aid.tag == AID_OBERTHUR_V5 ? 0x11 : 0x11; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB6); + apdu.lc = sizeof(rsa_sbuf); + apdu.datalen = sizeof(rsa_sbuf); + apdu.data = rsa_sbuf; + } + else if (env->operation == SC_SEC_OPERATION_DECIPHER) { + rsa_sbuf[2] = prv->aid.tag == AID_OBERTHUR_V5 ? 0x11 : 0x02; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xB8); + apdu.lc = sizeof(rsa_sbuf); + apdu.datalen = sizeof(rsa_sbuf); + apdu.data = rsa_sbuf; + } + else { + sc_error(card->ctx, "Invalid crypto operation: %X\n", env->operation); + return SC_ERROR_NOT_SUPPORTED; + } + + break; + default: + sc_error(card->ctx, "Invalid crypto algorithm supplied.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + + senv->algorithm = env->algorithm; + senv->key_file_id = key_file->id; + senv->key_size = key_file->size; + + if (key_file) + sc_file_free(key_file); + + return 0; +} + + +static int +auth_restore_security_env(struct sc_card *card, int se_num) +{ + return 0; +} + + +static int +auth_compute_signature(struct sc_card *card, + const u8 *in, size_t ilen, u8 * out, size_t olen) +{ + auth_senv_t *senv = &((struct auth_private_data *) card->drv_data)->senv; + struct sc_apdu apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + int rv; + + sc_debug(card->ctx, " inlen %i, outlen %i, algo %i\n", ilen, olen, senv->algorithm); + if (!senv->key_file_id) + return SC_ERROR_INVALID_DATA; + + switch (senv->algorithm) { + case SC_ALGORITHM_RSA: + sc_debug(card->ctx, "algorithm SC_ALGORITHM_RSA\n"); + if (ilen > 96) { + sc_error(card->ctx, "Illegal input length for CosmopolIC v4: %d\n", ilen); + return SC_ERROR_INVALID_ARGUMENTS; + } + else if (olen < ilen) { + sc_error(card->ctx, "Output buffer too small.\n"); + return SC_ERROR_BUFFER_TOO_SMALL; + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0x9A); + apdu.lc = ilen; + apdu.datalen = ilen; + apdu.data = sbuf; + memcpy(sbuf, in, ilen); + apdu.le = senv->key_size/8; + + apdu.resp = malloc(senv->key_size/8+8); + if (apdu.resp==NULL) + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + apdu.resplen = senv->key_size/8; + break; + default: + sc_error(card->ctx, "Invalid crypto algorithm supplied.\n"); + return SC_ERROR_NOT_SUPPORTED; + } + + + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + + if (apdu.resplen != senv->key_size/8) { + sc_error(card->ctx, "Signature failed: invalide response length %i\n", + apdu.resplen); + return SC_ERROR_CARD_CMD_FAILED; + } + + memcpy(out, apdu.resp, apdu.resplen); + + if (card->ctx->debug >= 5) { + char debug_buf[2048]; + + debug_buf[0] = 0; + if (!apdu.sensitive || card->ctx->debug >= 6) + sc_hex_dump(card->ctx, in, ilen, debug_buf, sizeof(debug_buf)); + sc_debug(card->ctx, "auth_compute_signature in %d bytes :\n%s", + ilen, apdu.sensitive ? ", sensitive" : "", debug_buf); + + debug_buf[0] = 0; + if (!apdu.sensitive || card->ctx->debug >= 6) + sc_hex_dump(card->ctx, out, apdu.resplen, debug_buf, sizeof(debug_buf)); + sc_debug(card->ctx, "auth_compute_signature out %d bytes :\n%s", + apdu.resplen, + apdu.sensitive ? ", sensitive" : "", debug_buf); + } + + sc_debug(card->ctx, "Signature Template return %i\n", apdu.resplen); + return apdu.resplen; +} + +static int +auth_decipher(struct sc_card *card, const u8 * crgram, size_t crgram_len, + u8 * out, size_t outlen) +{ + auth_senv_t *senv = &((struct auth_private_data *) card->drv_data)->senv; + struct auth_private_data *prv = card->drv_data; + struct sc_apdu apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + int rv; + + sc_debug(card->ctx,": crgram_len %i; outlen %i\n", crgram_len, outlen); + if (!out || !outlen || crgram_len > SC_MAX_APDU_BUFFER_SIZE) + return SC_ERROR_INVALID_ARGUMENTS; + + if (!senv->key_file_id) + return SC_ERROR_INVALID_DATA; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); + apdu.data = sbuf; + apdu.resp = malloc(SC_MAX_APDU_BUFFER_SIZE); + if (!apdu.resp) + return SC_ERROR_OUT_OF_MEMORY; + apdu.resplen = SC_MAX_APDU_BUFFER_SIZE; + + switch (senv->algorithm) { + case SC_ALGORITHM_RSA: + sc_debug(card->ctx, "algorithm SC_ALGORITHM_RSA\n"); + if (crgram_len != 64 && crgram_len != 128 && crgram_len != 256) { + rv = SC_ERROR_INVALID_ARGUMENTS; + goto done; + } + + if (senv->key_size==2048) { + int nn; + if (prv->aid.tag == AID_OBERTHUR_V5) + nn = 8; + else + nn = 1; + + apdu.cla |= 0x10; + memcpy(sbuf, crgram, nn); + apdu.lc = nn; + apdu.datalen = nn; + apdu.le = outlen; + + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + + crgram_len -= nn; + crgram += nn; + + apdu.cla &= ~0x10; + } + + break; + case SC_ALGORITHM_DES: + case SC_ALGORITHM_3DES: + sc_debug(card->ctx,"algorithm SC_ALGORITHM_DES\n"); + if (crgram_len == 0 || (crgram_len%8) != 0) { + rv = SC_ERROR_INVALID_ARGUMENTS; + goto done; + } + break; + default: + sc_error(card->ctx, "Invalid crypto algorithm supplied.\n"); + rv = SC_ERROR_NOT_SUPPORTED; + goto done; + } + + apdu.data = sbuf; + memcpy(sbuf, crgram, crgram_len); + apdu.lc = crgram_len; + apdu.datalen = crgram_len; + apdu.le = outlen; + apdu.resplen = SC_MAX_APDU_BUFFER_SIZE; + + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + + if (apdu.resplen > outlen) { + sc_error(card->ctx, "invalide response length %i\n", apdu.resplen); + rv = SC_ERROR_CARD_CMD_FAILED; + goto done; + } + + memcpy(out, apdu.resp, apdu.resplen); + rv = apdu.resplen; + +done: + if (apdu.resp) + free(apdu.resp); + + sc_debug(card->ctx, "return decipher len %i\n", rv); + return rv; +} + +/* Return the default AAK for this type of card */ +static int +auth_get_default_key(struct sc_card *card, struct sc_cardctl_default_key *data) +{ + return SC_ERROR_NO_DEFAULT_KEY; +} + + +static int +auth_encode_exponent(unsigned long exponent, u8 *buff, size_t buff_len) +{ + int ii, shift; + + for (shift=0; exponent >> (shift+8); shift += 8) + ; + + for (ii = 0; ii=0 ; ii++, shift-=8) + *(buff + ii) = (exponent >> shift) & 0xFF; + + if (ii==buff_len) + return 0; + else + return ii; +} + + +/* Generate key on-card */ +static int +auth_generate_key(sc_card_t *card, struct sc_cardctl_oberthur_genkey_info *data) +{ + struct sc_apdu apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + int rv = 0; + + sc_debug(card->ctx, " %i bits\n",data->key_bits); + if (data->key_bits < 512 || data->key_bits > 2048 || + (data->key_bits%0x20)!=0) { + sc_error(card->ctx, "Illegal key length: %d\n", data->key_bits); + return SC_ERROR_INVALID_ARGUMENTS; + } + + sbuf[0] = (data->id_pub >> 8) & 0xFF; + sbuf[1] = data->id_pub & 0xFF; + sbuf[2] = (data->id_prv >> 8) & 0xFF; + sbuf[3] = data->id_prv & 0xFF; + if (data->exponent != 0x10001) { + rv = auth_encode_exponent(data->exponent, &sbuf[5],SC_MAX_APDU_BUFFER_SIZE-6); + if (!rv) { + sc_error(card->ctx, "Cannot encode exponent\n"); + return SC_ERROR_INVALID_ARGUMENTS; + } + sbuf[4] = rv; + rv++; + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x46, 0x00, 0x00); + if (!(apdu.resp = malloc(data->key_bits/8+8))) { + sc_error(card->ctx, "Cannot allocate memory\n"); + return SC_ERROR_OUT_OF_MEMORY; + } + apdu.resplen = data->key_bits/8+8; + apdu.lc = rv + 4; + apdu.le = data->key_bits/8; + apdu.data = sbuf; + apdu.datalen = rv + 4;; + + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + if (apdu.resplen == 0) { + struct auth_private_data *prv = card->drv_data; + + rv = auth_read_component(card, SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC, + prv->aid.tag == AID_OBERTHUR_V5 ? 1 : 2, + apdu.resp, data->key_bits/8); + SC_TEST_RET(card->ctx, rv, "auth_read_component() returned error"); + if (rv<0) + return rv; + + apdu.resplen = rv; + } + if (data->pubkey) { + if (data->pubkey_len < apdu.resplen) + return SC_ERROR_INVALID_ARGUMENTS; + memcpy(data->pubkey,apdu.resp,apdu.resplen); + } + + data->pubkey_len = apdu.resplen; + free(apdu.resp); + + sc_debug(card->ctx, "resulted public key len %i\n", apdu.resplen); + return 0; +} + + +static int +auth_update_component(sc_card_t *card, struct sc_cardctl_oberthur_updatekey_info *args) +{ + struct sc_apdu apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE + 0x10]; + u8 ins, p1, p2; + int rv, len; + struct auth_private_data *prv = card->drv_data; + + sc_debug(card->ctx, ": nn %i; len %i\n", args->component, args->len); + if (args->len > sizeof(sbuf) || args->len > 0x100) + return SC_ERROR_INVALID_ARGUMENTS; + + sc_debug(card->ctx, "nn %i; len %i\n", args->component, args->len); + ins = 0xD8; + p1 = args->component; + p2 = 0x04; + len = 0; + if (prv->aid.tag == AID_OBERTHUR_V5) { + sc_debug(card->ctx, "nn %i; len %i\n", args->component, args->len); + sbuf[len++] = args->type; + sbuf[len++] = args->len; + memcpy(sbuf + len, args->data, args->len); + len += args->len; + + if (args->type == SC_CARDCTL_OBERTHUR_KEY_DES) { + unsigned char in[8]; + unsigned char out[8]; + DES_cblock kk; + DES_key_schedule ks; + + assert(DES_KEY_SZ==8); + + if (args->len!=8 && args->len!=24) + return SC_ERROR_INVALID_ARGUMENTS; + + p2 = 0; + memset(in, 0, sizeof(in)); + memcpy(&kk, args->data, 8); + DES_set_key_unchecked(&kk,&ks); + DES_ecb_encrypt((DES_cblock *)in, (DES_cblock *)out, &ks, DES_ENCRYPT); + if (args->len==24) { + sc_debug(card->ctx, "nn %i; len %i\n", args->component, args->len); + memcpy(&kk, args->data + 8, 8); + DES_set_key_unchecked(&kk,&ks); + memcpy(in, out, 8); + DES_ecb_encrypt((DES_cblock *)in, (DES_cblock *)out, &ks, DES_DECRYPT); + + sc_debug(card->ctx, "nn %i; len %i\n", args->component, args->len); + memcpy(&kk, args->data + 16, 8); + DES_set_key_unchecked(&kk,&ks); + memcpy(in, out, 8); + DES_ecb_encrypt((DES_cblock *)in, (DES_cblock *)out, &ks, DES_ENCRYPT); + } + + sbuf[len++] = 0x03; + memcpy(sbuf + len, out, 3); + len += 3; + } + else { + sbuf[len++] = 0; + } + } + else { + ins = 0xDC; + memcpy(sbuf + len, args->data, args->len); + len += args->len; + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, ins, p1, p2); + apdu.cla |= 0x80; + apdu.data = sbuf; + apdu.datalen = len; + apdu.lc = len; + apdu.sensitive = 1; + if (args->len == 0x100) { + if (prv->aid.tag == AID_OBERTHUR_V5) { + sbuf[0] = args->type; + sbuf[1] = 0x20; + memcpy(sbuf + 2, args->data, 0x20); + sbuf[0x22] = 0; + apdu.cla |= 0x10; + apdu.data = sbuf; + apdu.datalen = 0x23; + apdu.lc = 0x23; + rv = sc_transmit_apdu(card, &apdu); + apdu.cla &= ~0x10; + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + sbuf[0] = args->type; + sbuf[1] = 0xE0; + memcpy(sbuf + 2, args->data + 0x20, 0xE0); + sbuf[0xE2] = 0; + apdu.data = sbuf; + apdu.datalen = 0xE3; + apdu.lc = 0xE3; + } + else { + apdu.cla |= 0x10; + apdu.datalen = 1; + apdu.lc = 1; + rv = sc_transmit_apdu(card, &apdu); + apdu.cla &= ~0x10; + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + apdu.data = sbuf + 1; + apdu.datalen = 255; + apdu.lc = 255; + } + } + + rv = sc_transmit_apdu(card, &apdu); + memset(sbuf, 0, sizeof(sbuf)); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card return error"); + return rv; +} + + + static int +auth_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) +{ + switch (cmd) { + case SC_CARDCTL_GET_DEFAULT_KEY: + return auth_get_default_key(card, + (struct sc_cardctl_default_key *) ptr); + case SC_CARDCTL_OBERTHUR_GENERATE_KEY: + return auth_generate_key(card, + (struct sc_cardctl_oberthur_genkey_info *) ptr); + case SC_CARDCTL_OBERTHUR_UPDATE_KEY: + return auth_update_component(card, + (struct sc_cardctl_oberthur_updatekey_info *) ptr); + case SC_CARDCTL_OBERTHUR_CREATE_PIN: + return auth_create_reference_data(card, + (struct sc_cardctl_oberthur_createpin_info *) ptr); + default: + return SC_ERROR_NOT_SUPPORTED; + } +} + + +static int +auth_read_component(sc_card_t *card, enum SC_CARDCTL_OBERTHUR_KEY_TYPE type, + int num, unsigned char *out, size_t outlen) +{ + int rv; + struct sc_apdu apdu; + struct auth_private_data *prv = card->drv_data; + unsigned char resp[SC_MAX_APDU_BUFFER_SIZE]; + + sc_debug(card->ctx, ": num %i, outlen %i, type %i\n", num, outlen, type); + if (type!=SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) + return SC_ERROR_NO_CARD_SUPPORT; + else if (!outlen) + return SC_ERROR_INCORRECT_PARAMETERS; + + if (prv->aid.tag == AID_OBERTHUR_V5) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB4, num, 0x00); + apdu.cla |= 0x80; + apdu.le = outlen; + apdu.resp = resp; + apdu.resplen = sizeof(resp); + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + + if (outlen < apdu.resplen) + return SC_ERROR_WRONG_LENGTH; + + memcpy(out, apdu.resp, apdu.resplen); + return apdu.resplen; + } + else { + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB2, num, 0x04); + apdu.cla |= 0x80; + apdu.le = outlen; + apdu.resp = resp; + apdu.resplen = sizeof(resp); + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + + if (outlen < apdu.resplen) + return SC_ERROR_WRONG_LENGTH; + + memcpy(out, apdu.resp, apdu.resplen); + return apdu.resplen; + } +} + + +static int auth_get_pin_reference (struct sc_card *card, + int type, int reference, int cmd, int *out_ref) +{ + struct auth_private_data *prv = card->drv_data; + + if (!card || !out_ref) + return SC_ERROR_INVALID_ARGUMENTS; + + switch (prv->aid.tag) { + case AID_OBERTHUR_V5 : + switch (type) { + case SC_AC_CHV: + if (reference == 1) { + if (cmd==SC_PIN_CMD_VERIFY) + *out_ref = 0x81; + else + *out_ref = 0x01; + } + else if (reference == 2) + *out_ref = 0x04; + else + return SC_ERROR_INVALID_PIN_REFERENCE; + break; + + default: + return SC_ERROR_INVALID_ARGUMENTS; + } + break; + default: + return SC_ERROR_INVALID_ARGUMENTS; + } + + return 0; +} + + +static void +auth_init_pin_info(struct sc_card *card, struct sc_pin_cmd_pin *pin, + unsigned int type) +{ + struct auth_private_data *data = card->drv_data; + + pin->offset = 0; + pin->pad_char = 0xFF; + pin->encoding = SC_PIN_ENCODING_ASCII; + + switch (data->aid.tag) { + case AID_OBERTHUR_V5 : + if (type==AUTH_PIN) { + pin->max_length = 64; + pin->pad_length = 64; + } + else { + pin->max_length = 16; + pin->pad_length = 16; + } + break; + } +} + + +static int +auth_verify(struct sc_card *card, unsigned int type, + int ref, const u8 *data, size_t data_len, int *tries_left) +{ + sc_apdu_t apdu; + int rv, pin_ref; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + struct sc_pin_cmd_pin pinfo; + + sc_debug(card->ctx,": type %i; ref %i, data_len %i\n", type, ref, data_len); + if (ref == 3) { + rv = auth_get_pin_reference (card, type, 1, SC_PIN_CMD_VERIFY, &pin_ref); + if (rv) + return rv; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, pin_ref); + apdu.lc = 0x0; + apdu.le = 0x0; + apdu.resplen = 0; + apdu.resp = NULL; + apdu.p2 = pin_ref; + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) { + rv = auth_get_pin_reference (card, type, 2, SC_PIN_CMD_VERIFY, &pin_ref); + if (rv) + return rv; + + apdu.p2 = pin_ref; + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + } + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00 ) { + if (data && data_len > 1 && *data!=ref && !isalnum(*data)) { + rv = auth_verify(card, type, *data, + data+1, data_len - 1, tries_left); + } + } + + return rv; + } + + rv = auth_get_pin_reference (card, type, ref, SC_PIN_CMD_VERIFY, &pin_ref); + if (rv) + return rv; + sc_debug(card->ctx, " pin ref %X\n", pin_ref); + + auth_init_pin_info(card, &pinfo, AUTH_PIN); + if (data_len > pinfo.pad_length) + return SC_ERROR_INVALID_ARGUMENTS; + + memset(sbuf, pinfo.pad_char, pinfo.pad_length); + memcpy(sbuf, data, data_len); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, pin_ref); + apdu.data = sbuf; + apdu.datalen = pinfo.pad_length; + apdu.lc = pinfo.pad_length; + apdu.sensitive = 1; + rv = sc_transmit_apdu(card, &apdu); + memset(sbuf, 0, sizeof(sbuf)); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + if (tries_left && apdu.sw1 == 0x63 && (apdu.sw2 & 0xF0) == 0xC0) + *tries_left = apdu.sw2 & 0x0F; + + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + return rv; +} + + +static int +auth_change_reference_data (struct sc_card *card, unsigned int type, + int ref, const u8 *old, size_t oldlen, + const u8 *new, size_t newlen, int *tries_left) +{ + sc_apdu_t apdu; + int rv, pin_ref; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + struct sc_pin_cmd_pin pinfo; + + rv = auth_get_pin_reference (card, type, ref, SC_PIN_CMD_CHANGE, &pin_ref); + if (rv) + return rv; + sc_debug(card->ctx, " pin ref %X\n", pin_ref); + + auth_init_pin_info(card, &pinfo, AUTH_PIN); + + if (oldlen > pinfo.pad_length || newlen > pinfo.pad_length) + return SC_ERROR_INVALID_ARGUMENTS; + + memset(sbuf, pinfo.pad_char, pinfo.pad_length * 2); + memcpy(sbuf, old, oldlen); + memcpy(sbuf + pinfo.pad_length, new, newlen); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0, pin_ref); + apdu.data = sbuf; + apdu.datalen = pinfo.pad_length * 2; + apdu.lc = pinfo.pad_length * 2; + apdu.sensitive = 1; + + rv = sc_transmit_apdu(card, &apdu); + memset(sbuf, 0, sizeof(sbuf)); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + if (tries_left && apdu.sw1 == 0x63 && (apdu.sw2 & 0xF0) == 0xC0) + *tries_left = apdu.sw2 & 0x0F; + + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + return rv; +} + + +static int +auth_reset_retry_counter(struct sc_card *card, unsigned int type, + int ref, const u8 *puk, size_t puklen, + const u8 *pin, size_t pinlen) +{ + sc_apdu_t apdu; + int rv, pin_ref, len; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + struct sc_pin_cmd_pin pin_info, puk_info; + + rv = auth_get_pin_reference (card, type, ref, SC_PIN_CMD_UNBLOCK, &pin_ref); + if (rv) + return rv; + sc_debug(card->ctx, " pin ref %X\n", pin_ref); + + auth_init_pin_info(card, &puk_info, AUTH_PUK); + auth_init_pin_info(card, &pin_info, AUTH_PIN); + + if (puklen > puk_info.pad_length || pinlen > pin_info.pad_length) + return SC_ERROR_INVALID_ARGUMENTS; + + memset(sbuf, puk_info.pad_char, puk_info.pad_length); + memcpy(sbuf, puk, puklen); + len = puk_info.pad_length; + if (pin && pinlen) { + memset(sbuf + len, pin_info.pad_char, pin_info.pad_length); + memcpy(sbuf + len, pin, pinlen); + len += pin_info.pad_length; + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2C, + len == puk_info.pad_length ? 1 : 0, pin_ref); + apdu.data = sbuf; + apdu.datalen = len; + apdu.lc = len; + apdu.sensitive = 1; + + rv = sc_transmit_apdu(card, &apdu); + memset(sbuf, 0, sizeof(sbuf)); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + return rv; +} + + +static int +auth_create_reference_data (struct sc_card *card, + struct sc_cardctl_oberthur_createpin_info *args) +{ + sc_apdu_t apdu; + int rv, pin_ref, len; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + struct sc_pin_cmd_pin pin_info, puk_info; + struct auth_private_data *prv = card->drv_data; + + if (args->pin_tries < 1 || !args->pin || !args->pin_len) + return SC_ERROR_INVALID_ARGUMENTS; + + if (prv->aid.tag == AID_OBERTHUR_V5 && args->type == SC_AC_CHV) { + if (args->ref == 1) + pin_ref = 0x01; + else if (args->ref == 2) + pin_ref = 0x04; + else + return SC_ERROR_INVALID_PIN_REFERENCE; + } + else + return SC_ERROR_INVALID_ARGUMENTS; + + + sc_debug(card->ctx, " pin ref %X\n", pin_ref); + + auth_init_pin_info(card, &puk_info, AUTH_PUK); + auth_init_pin_info(card, &pin_info, AUTH_PIN); + + if (args->puk && args->puk_len && (args->puk_len%puk_info.pad_length)) + return SC_ERROR_INVALID_ARGUMENTS; + + len = 0; + sbuf[len++] = args->pin_tries; + sbuf[len++] = pin_info.pad_length; + memset(sbuf + len, pin_info.pad_char, pin_info.pad_length); + memcpy(sbuf + len, args->pin, args->pin_len); + len += pin_info.pad_length; + + if (args->puk && args->puk_len) { + sbuf[len++] = args->puk_tries; + sbuf[len++] = args->puk_len / puk_info.pad_length; + memcpy(sbuf + len, args->puk, args->puk_len); + len += args->puk_len; + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 1, pin_ref); + apdu.data = sbuf; + apdu.datalen = len; + apdu.lc = len; + apdu.sensitive = 1; + + rv = sc_transmit_apdu(card, &apdu); + memset(sbuf, 0, sizeof(sbuf)); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + return rv; +} + + +static int +auth_logout(struct sc_card *card) +{ + sc_apdu_t apdu; + int rv, pin_ref; + struct auth_private_data *data = card->drv_data; + int reset_flag = data->aid.tag == AID_OBERTHUR_V5 ? 0x20 : 0x00; + + rv = auth_get_pin_reference (card, SC_AC_CHV, 1, SC_PIN_CMD_UNBLOCK, &pin_ref); + if (rv) + return rv; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2E, 0x00, 0x00); + apdu.cla = 0x80; + apdu.lc = 0x0; + apdu.le = 0x0; + apdu.resplen = 0; + apdu.resp = NULL; + apdu.p2 = pin_ref | reset_flag; + rv = sc_transmit_apdu(card, &apdu); + + rv = auth_get_pin_reference (card, SC_AC_CHV, 2, SC_PIN_CMD_UNBLOCK, &pin_ref); + if (rv) + return rv; + + apdu.p2 = pin_ref | reset_flag; + rv = sc_transmit_apdu(card, &apdu); + + return rv; +} + +static int +write_publickey (struct sc_card *card, unsigned int offset, + const u8 *buf, size_t count) +{ + int ii, rv; + struct sc_pkcs15_pubkey_rsa key; + int len = 0, der_size = 0; + struct sc_cardctl_oberthur_updatekey_info args; + + if (card->ctx->debug >= 5) { + char debug_buf[2048]; + + debug_buf[0] = 0; + sc_hex_dump(card->ctx, buf, count, debug_buf, sizeof(debug_buf)); + sc_debug(card->ctx, "write_publickey in %d bytes :\n%s", count, debug_buf); + } + + if (offset > sizeof(rsa_der)) + return SC_ERROR_INVALID_ARGUMENTS; + + len = offset+count > sizeof(rsa_der) ? sizeof(rsa_der) - offset : count; + + memcpy(rsa_der + offset, buf, len); + rsa_der_len = offset + len; + + if (rsa_der[0]==0x30) { + if (rsa_der[1] & 0x80) + for (ii=0; ii < (rsa_der[1]&0x0F); ii++) + der_size = der_size*0x100 + rsa_der[2+ii]; + else + der_size = rsa_der[1]; + } + + sc_debug(card->ctx, " der_size %i\n",der_size); + if (offset + len < der_size + 2) + return len; + + rv = sc_pkcs15_decode_pubkey_rsa(card->ctx, &key, rsa_der, rsa_der_len); + rsa_der_len = 0; + memset(rsa_der, 0, sizeof(rsa_der)); + if (rv) { + sc_error(card->ctx, " cannot decode public key\n"); + return SC_ERROR_INVALID_ASN1_OBJECT; + } + + memset(&args, 0, sizeof(args)); + args.type = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; + args.component = 1; + args.data = key.modulus.data; + args.len = key.modulus.len; + rv = auth_update_component(card, &args); + if (rv) + goto end; + + memset(&args, 0, sizeof(args)); + args.type = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; + args.component = 2; + args.data = key.exponent.data; + args.len = key.exponent.len; + rv = auth_update_component(card, &args); + if (rv >= 0) + rv = len; + +end: + card->cla &= ~0x80; + return rv; +} + + +static int +auth_update_binary(struct sc_card *card, unsigned int offset, + const u8 *buf, size_t count, unsigned long flags) +{ + int rv = 0; + + sc_debug(card->ctx, "; offset %i; count %i\n", offset, count); + sc_debug(card->ctx, "; last selected : magic %X; ef %X\n", + last_selected_file.magic, last_selected_file.ef_structure); + if (offset & ~0x7FFF) { + sc_error(card->ctx, "Invalid file offset %u",offset); + return SC_ERROR_INVALID_ARGUMENTS; + } + + if (last_selected_file.magic==SC_FILE_MAGIC && + last_selected_file.ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + rv = write_publickey(card, offset, buf, count); + } + else if (last_selected_file.magic==SC_FILE_MAGIC && + last_selected_file.ef_structure == SC_CARDCTL_OBERTHUR_KEY_DES) { + struct sc_cardctl_oberthur_updatekey_info args; + + memset(&args, 0, sizeof(args)); + args.type = SC_CARDCTL_OBERTHUR_KEY_DES; + args.component = 0; + args.data = (unsigned char *)buf; + args.len = count; + rv = auth_update_component(card, &args); + } + else { + rv = iso_ops->update_binary(card, offset, buf, count, 0); + } + + SC_TEST_RET(card->ctx, rv, "Card returned error"); + return rv; +} + + +static int +auth_read_binary(struct sc_card *card, unsigned int offset, + u8 *buf, size_t count, unsigned long flags) +{ + int rv; + struct auth_private_data *data = card->drv_data; + + sc_debug(card->ctx,"; offset %i; size %i; flags 0x%lX\n", offset, count, flags); + sc_debug(card->ctx,"; last selected : magic %X; ef %X\n", + last_selected_file.magic, last_selected_file.ef_structure); + if (offset & ~0x7FFF) { + sc_error(card->ctx, "Invalid file offset %u",offset); + return SC_ERROR_INVALID_ARGUMENTS; + } + + if (last_selected_file.magic==SC_FILE_MAGIC && + last_selected_file.ef_structure == SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC) { + int jj; + unsigned char resp[0x100], *out = NULL; + size_t resp_len, out_len; + struct sc_pkcs15_bignum bn[2]; + struct sc_pkcs15_pubkey_rsa key; + + resp_len = sizeof(resp); + rv = auth_read_component(card, SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC, + data->aid.tag == AID_OBERTHUR_V5 ? 2 : 1, + resp, resp_len); + if (rv<0) + return rv; + + for (jj=0; jjaid.tag == AID_OBERTHUR_V5 ? 1 : 2, + resp, resp_len); + if (rv <= 0) + return rv; + bn[1].data = malloc(rv); + bn[1].len = rv; + memcpy(bn[1].data, resp, rv); + + key.exponent = bn[0]; + key.modulus = bn[1]; + + if (sc_pkcs15_encode_pubkey_rsa(card->ctx, &key, &out, &out_len)) { + sc_error(card->ctx, "cannot decode public key\n"); + rv = SC_ERROR_INVALID_ASN1_OBJECT; + } + else { + rv = out_len - offset > count ? count : out_len - offset; + memcpy(buf, out + offset, rv); + if (card->ctx->debug >= 5) { + char debug_buf[2048]; + + debug_buf[0] = 0; + sc_hex_dump(card->ctx, buf, rv, debug_buf, sizeof(debug_buf)); + sc_debug(card->ctx, "write_publickey in %d bytes :\n%s", + count, debug_buf); + } + } + + if (bn[0].data) free(bn[0].data); + if (bn[1].data) free(bn[1].data); + if (out) free(out); + } + else { + rv = iso_ops->read_binary(card, offset, buf, count, 0); + } + + SC_TEST_RET(card->ctx, rv, "Card returned error"); + return rv; +} + + +static int +auth_delete_record(struct sc_card *card, unsigned int nr_rec) +{ + int rv = 0; + struct sc_apdu apdu; + + sc_debug(card->ctx, "auth_delete_record(): nr_rec %i\n", nr_rec); + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x32, nr_rec, 0x04); + apdu.cla = 0x80; + apdu.lc = 0x0; + apdu.le = 0x0; + apdu.resplen = 0; + apdu.resp = NULL; + + rv = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, rv, "APDU transmit failed"); + + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, rv, "Card returned error"); + return rv; +} + + + +static struct sc_card_driver * +sc_get_driver(void) +{ + if (iso_ops == NULL) + iso_ops = sc_get_iso7816_driver()->ops; + + auth_ops = *iso_ops; + auth_ops.match_card = auth_match_card; + auth_ops.init = auth_init; + auth_ops.finish = auth_finish; + auth_ops.select_file = auth_select_file; + auth_ops.list_files = auth_list_files; + auth_ops.delete_file = auth_delete_file; + auth_ops.create_file = auth_create_file; + auth_ops.read_binary = auth_read_binary; + auth_ops.update_binary = auth_update_binary; + auth_ops.delete_record = auth_delete_record; + auth_ops.card_ctl = auth_card_ctl; + auth_ops.set_security_env = auth_set_security_env; + auth_ops.restore_security_env = auth_restore_security_env; + auth_ops.compute_signature = auth_compute_signature; + auth_ops.decipher = auth_decipher; + + auth_ops.pin_cmd = NULL; + auth_ops.verify = auth_verify; + auth_ops.reset_retry_counter = auth_reset_retry_counter; + auth_ops.change_reference_data = auth_change_reference_data; + + auth_ops.logout = auth_logout; + return &auth_drv; +} + + +struct sc_card_driver * +sc_get_oberthur_driver(void) +{ + return sc_get_driver(); +} + +#endif /* HAVE_OPENSSL */ diff --git a/src/libopensc/card-oberthur.h b/src/libopensc/card-oberthur.h new file mode 100644 index 00000000..fb2bf7f9 --- /dev/null +++ b/src/libopensc/card-oberthur.h @@ -0,0 +1,70 @@ +#ifndef _OPENSC_CARD_OBERTHUR_H +#define _OPENSC_CARD_OBERTHUR_H + +#include + +#define AID_OBERTHUR_V2 0x201 +#define AID_OBERTHUR_V4 0x401 +#define AID_OBERTHUR_V5 0x501 + +#define ATR_OBERTHUR 0x0100 +#define ATR_OBERTHUR_32K 0x0110 +#define ATR_OBERTHUR_32K_BIO 0x0112 +#define ATR_OBERTHUR_64K 0x0120 + +#define FLAG_KEYGEN 0x0001 + +#define AUTH_PIN 1 +#define AUTH_PUK 2 + +#define PUBKEY_512_ASN1_SIZE 0x4A +#define PUBKEY_1024_ASN1_SIZE 0x8C +#define PUBKEY_2048_ASN1_SIZE 0x10E + +#define SC_OBERTHUR_MAX_ATTR_SIZE 8 + +struct NTLV { + char *name; + unsigned int tag; + int len; + unsigned char *value; +}; +typedef struct NTLV NTLV_t; + +struct oberthur_atr { + const char *atr; + const char *name; + unsigned int type; +}; + +struct oberthur_aid { + const char *aid; + const char *name; + unsigned int type; +}; +typedef struct oberthur_aid oberthur_aid_t; + +struct auth_application_id { + unsigned int tag; + u8 value[SC_MAX_AID_SIZE]; + int len; +}; +typedef struct auth_application_id auth_application_id_t; + +struct auth_senv { + unsigned int algorithm; + int key_file_id; + size_t key_size; +}; +typedef struct auth_senv auth_senv_t; + + +struct auth_private_data { + struct sc_pin_cmd_pin pin_info; + long int sn; + auth_application_id_t aid; + auth_senv_t senv; +}; +typedef struct auth_private_data auth_private_data_t; + +#endif /* _OPENSC_CARD_OBERTHUR_H */ diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index 104749f1..181bd86c 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -85,6 +85,13 @@ enum { SC_CARDCTL_JCOP_LOCK, SC_CARDCTL_JCOP_GENERATE_KEY, + /* + * Oberthur specific calls + */ + SC_CARDCTL_OBERTHUR_BASE = _CTL_PREFIX('O', 'B', 'R'), + SC_CARDCTL_OBERTHUR_UPDATE_KEY, + SC_CARDCTL_OBERTHUR_GENERATE_KEY, + SC_CARDCTL_OBERTHUR_CREATE_PIN, }; enum { @@ -234,6 +241,44 @@ struct sc_cardctl_jcop_genkey { unsigned int pubkey_len; }; +/* + * Oberthur ex_data stuff + */ +enum SC_CARDCTL_OBERTHUR_KEY_TYPE { + SC_CARDCTL_OBERTHUR_KEY_DES = 0x80, + + SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC = 0xA1, + SC_CARDCTL_OBERTHUR_KEY_RSA_SFM, + SC_CARDCTL_OBERTHUR_KEY_RSA_CRT, + SC_CARDCTL_OBERTHUR_KEY_DSA_PUBLIC, + SC_CARDCTL_OBERTHUR_KEY_DSA_PRIVATE, +}; + +struct sc_cardctl_oberthur_genkey_info { + unsigned int id_prv, id_pub; + unsigned int key_bits; + unsigned long exponent; + unsigned char * pubkey; + unsigned int pubkey_len; +}; + +struct sc_cardctl_oberthur_updatekey_info { + enum SC_CARDCTL_OBERTHUR_KEY_TYPE type; + unsigned int component; + unsigned char *data; + unsigned int len; +}; + +struct sc_cardctl_oberthur_createpin_info { + unsigned int type; + unsigned int ref; + unsigned char *pin; + unsigned int pin_len; + unsigned int pin_tries; + unsigned char *puk; + unsigned int puk_len; + unsigned int puk_tries; +}; #ifdef __cplusplus } diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index 90f1c2a6..3541c41d 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -69,6 +69,9 @@ static const struct _sc_driver_entry internal_card_drivers[] = { { "tcos", (void *) sc_get_tcos_driver, NULL }, { "opengpg", (void *) sc_get_openpgp_driver, NULL }, { "jcop", (void *) sc_get_jcop_driver, NULL }, +#ifdef HAVE_OPENSSL + { "oberthur", (void *) sc_get_oberthur_driver, NULL }, +#endif /* The default driver should be last, as it handles all the * unrecognized cards. */ { "default", (void *) sc_get_default_driver, NULL }, diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index 814b253b..3276bf5d 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -539,6 +539,7 @@ struct sc_card_operations { * restore_security_env. */ int (*decipher)(struct sc_card *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen); + /* compute_signature: Generates a digital signature on the card. Similiar * to the function decipher. */ int (*compute_signature)(struct sc_card *card, const u8 * data, @@ -579,9 +580,6 @@ struct sc_card_operations { int (*get_data)(sc_card_t *, unsigned int, u8 *, size_t); int (*put_data)(sc_card_t *, unsigned int, const u8 *, size_t); - /* FIXME - * Any of ISO-7816s contains this function? - */ int (*delete_record)(sc_card_t *card, unsigned int rec_nr); }; @@ -874,6 +872,7 @@ extern struct sc_card_driver *sc_get_starcos_driver(void); extern struct sc_card_driver *sc_get_tcos_driver(void); extern struct sc_card_driver *sc_get_openpgp_driver(void); extern struct sc_card_driver *sc_get_jcop_driver(void); +extern struct sc_card_driver *sc_get_oberthur_driver(void); #ifdef __cplusplus } diff --git a/src/pkcs15init/Makefile.am b/src/pkcs15init/Makefile.am index 4f2a34ee..3279be4c 100644 --- a/src/pkcs15init/Makefile.am +++ b/src/pkcs15init/Makefile.am @@ -14,8 +14,9 @@ PROFILES = \ miocos.profile \ etoken.profile \ jcop.profile \ + oberthur.profile \ starcos.profile \ - pkcs15.profile + pkcs15.profile EXTRA_DIST = $(PROFILES) Makefile.mak @@ -24,7 +25,9 @@ lib_LTLIBRARIES = libpkcs15init.la 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-etoken.c pkcs15-jcop.c pkcs15-starcos.c \ + pkcs15-oberthur.c + libpkcs15init_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ include_HEADERS = pkcs15-init.h diff --git a/src/pkcs15init/oberthur.profile b/src/pkcs15init/oberthur.profile new file mode 100644 index 00000000..e0d1459b --- /dev/null +++ b/src/pkcs15init/oberthur.profile @@ -0,0 +1,188 @@ +# +# PKCS15 r/w profile for Oberthur cards +# +cardinfo { + label = "S3M"; + manufacturer = "Oberthur/OpenSC"; + + max-pin-length = 64; + min-pin-length = 4; + pin-encoding = ascii-numeric; + pin-pad-char = 0xFF; + + # Delete or not the public key when inconporating the + # corresponding certificate. + keep-public-key = no; # yes/no + +} + +# Define reasonable limits for PINs and PUK +# Note that we do not set a file path or reference +# here; that is done dynamically. +PIN user-pin { + attempts = 5; + max-length = 64; + min-length = 4; + flags = 0x32; # local, initialized, needs-padding + reference = 1 +} +PIN user-puk { + attempts = 5; + max-length = 16; + min-length = 4; + flags = 0x32; # local, initialized, needs-padding +} +PIN so-pin { + auth-id = FF; + attempts = 3; + max-length = 64; + min-length = 4; + flags = 0xB2; + reference = 2 + default-value = "31:32:33:34:35:36:37:38"; +} + +# CHV3 used for Oberthur's specifique access condition "PIN or SOPIN" +# Any value for this pin can given, when the OpenSC tools are asking for. + +# Additional filesystem info. +# This is added to the file system info specified in the +# main profile. +filesystem { + DF MF { + ACL = *=CHV2; + + DF OberthurAWP-AppDF { + ACL = *=NONE; + ACL = CREATE=CHV2, CRYPTO=NEVER; + file-id = 5011; + size = 40; + + DF private-DF { + ACL = *=NEVER; + ACL = CREATE=CHV1, CRYPTO=CHV1, FILES=NONE, DELETE=NONE; + file-id = 9002; + size = 40; + + # Private RSA keys + EF OberthurAWP-private-key-info { + ACL = WRITE=CHV1, UPDATE=CHV1, READ=CHV1; + } + EF template-private-key { + file-id = 3000; + type = internal-ef; + # READ acl used instead of DECRYPT/SIGN + ACL = UPDATE=CHV1, READ=CHV1; + } + + # Private DES keys + EF OberthurAWP-private-des-info { + ACL = WRITE=CHV1, UPDATE=CHV1, READ=CHV1; + } + EF template-private-des { + file-id = 4000; + type = internal-ef; + size = 24; # 192 bits + # READ acl used insted of DECRYPT/ENCRYPT/CHECKSUM + ACL = UPDATE=CHV1, READ=CHV1; + } + + # Private data + EF OberthurAWP-private-data-info { + ACL = WRITE=CHV1, UPDATE=CHV1, READ=CHV1; + } + EF template-private-data { + file-id = 6000; + ACL = WRITE=CHV1, UPDATE=CHV1, READ=CHV1; + } + } + + DF public-DF { + ACL = CREATE=NONE, CRYPTO=NONE, FILES=NONE, DELETE=NONE; + file-id = 9001; + size = 80; + + # Certificate + EF OberthurAWP-certificate-info { + ACL = WRITE=NONE, UPDATE=NONE, READ=NONE, ERASE=NONE; + } + EF template-certificate { + file-id = 2000; + ACL = WRITE=NONE, UPDATE=NONE, READ=NONE, ERASE=NONE; + } + + #Public Key + EF OberthurAWP-public-key-info { + ACL = WRITE=NONE, UPDATE=NONE, READ=NONE, ERASE=NONE; + } + EF template-public-key { + file-id = 1000; + type = internal-ef; + ACL = *=NONE; + } + + # Public DES keys + EF OberthurAWP-public-des-info { + ACL = WRITE=NONE, UPDATE=NONE, READ=NONE, ERASE=NONE; + } + EF template-public-des { + file-id = 7000; + type = internal-ef; + size = 24; # 192 bits + ACL = *=NONE; + } + + # Public data + EF OberthurAWP-public-data-info { + ACL = WRITE=NONE, UPDATE=NONE, READ=NONE, ERASE=NONE; + } + EF template-public-data { + file-id = 5000; + ACL = *=NONE; + } + } + + EF OberthurAWP-token-info { + file-id = 1000; + size = 36; + ACL = WRITE=CHV2, UPDATE=CHV2, READ=NONE, ERASE=NEVER; + } + + EF OberthurAWP-container-list { + file-id = 3000; + structure = linear-variable; + size = 20; + record-length = 141; + ACL = WRITE=NONE, UPDATE=NONE, READ=NONE, ERASE=CHV3; + } + + EF OberthurAWP-public-list { + file-id = 4000; + size = 250; + ACL = *=NONE, ERASE=NEVER; + } + + EF OberthurAWP-private-list { + file-id = 5000; + size = 125; + ACL = WRITE=CHV1, UPDATE=CHV1, READ=NONE, ERASE=NEVER; + } + } + + DF PKCS15-AppDF { + ACL = *=CHV2, FILES=NONE; + size = 20; + + EF template-data-1 { + file-id = 3301; + ACL = *=CHV2, READ=NONE; + } + + EF template-data-2 { + file-id = 3302; + ACL = *=CHV2, READ=NONE; + } + } + } +} + diff --git a/src/pkcs15init/pkcs15-init.h b/src/pkcs15init/pkcs15-init.h index f954b947..3f4c5f94 100644 --- a/src/pkcs15init/pkcs15-init.h +++ b/src/pkcs15init/pkcs15-init.h @@ -365,6 +365,7 @@ extern struct sc_pkcs15init_operations *sc_pkcs15init_get_cyberflex_ops(void); extern struct sc_pkcs15init_operations *sc_pkcs15init_get_etoken_ops(void); 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); #ifdef __cplusplus } diff --git a/src/pkcs15init/pkcs15-lib.c b/src/pkcs15init/pkcs15-lib.c index 76fe599d..afedd116 100644 --- a/src/pkcs15init/pkcs15-lib.c +++ b/src/pkcs15init/pkcs15-lib.c @@ -89,6 +89,9 @@ static int sc_pkcs15init_update_tokeninfo(struct sc_pkcs15_card *, struct sc_profile *profile); static int sc_pkcs15init_update_odf(struct sc_pkcs15_card *, struct sc_profile *profile); +static int sc_pkcs15init_update_any_df(sc_pkcs15_card_t *p15card, + sc_profile_t *profile, + sc_pkcs15_df_t *df, int is_new); static sc_pkcs15_object_t *sc_pkcs15init_new_object(int type, const char *label, sc_pkcs15_id_t *auth_id, void *data); static int sc_pkcs15init_add_object(struct sc_pkcs15_card *, @@ -138,6 +141,7 @@ static struct profile_operations { { "etoken", (void *) sc_pkcs15init_get_etoken_ops }, { "jcop", (void *) sc_pkcs15init_get_jcop_ops }, { "starcos", (void *) sc_pkcs15init_get_starcos_ops }, + { "oberthur", (void *) sc_pkcs15init_get_oberthur_ops }, { NULL, NULL }, }; @@ -454,6 +458,9 @@ sc_pkcs15init_add_app(struct sc_card *card, struct sc_profile *profile, p15spec->card = card; + sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &puk_info); + sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &puk_info); + /* Perform card-specific initialization */ if (profile->ops->init_card && (r = profile->ops->init_card(profile, card)) < 0) { @@ -922,7 +929,7 @@ sc_pkcs15init_generate_key(struct sc_pkcs15_card *p15card, keybits, SC_ALGORITHM_ONBOARD_KEY_GEN)) return SC_ERROR_NOT_SUPPORTED; - if (profile->ops->generate_key == NULL) + if (profile->ops->generate_key == NULL && profile->ops->old_generate_key == NULL) return SC_ERROR_NOT_SUPPORTED; /* Set the USER PIN reference from args */ @@ -1347,6 +1354,7 @@ sc_pkcs15init_store_certificate(struct sc_pkcs15_card *p15card, return r; } + /* * Store a data object */ @@ -1407,6 +1415,7 @@ sc_pkcs15init_store_data(struct sc_pkcs15_card *p15card, { struct sc_file *file = NULL; int r; + unsigned int index = -1; /* Set the SO PIN reference from card */ if ((r = set_so_pin_from_card(p15card, profile)) < 0) @@ -1424,7 +1433,6 @@ sc_pkcs15init_store_data(struct sc_pkcs15_card *p15card, if (r < 0) return r; } else { - unsigned int index; /* Get the number of objects of this type already on this card */ index = sc_pkcs15_get_objects(p15card, @@ -1445,6 +1453,7 @@ sc_pkcs15init_store_data(struct sc_pkcs15_card *p15card, } r = sc_pkcs15init_update_file(profile, p15card->card, file, data->value, data->len); + *path = file->path; done: if (file) @@ -2114,12 +2123,6 @@ sc_pkcs15init_remove_object(sc_pkcs15_card_t *p15card, if ((df = obj->df) == NULL) return 0; -#ifdef notyet - if (profile->ops->ext_remove_data - && (r = profile->ops->ext_remove_data(profile, card, obj))) - return r; -#endif - /* Unlink the object and update the DF */ sc_pkcs15_remove_object(p15card, obj); if ((r = sc_pkcs15init_update_any_df(p15card, profile, df, 0)) < 0) @@ -2261,7 +2264,7 @@ do_get_and_verify_secret(sc_profile_t *pro, sc_card_t *card, const char *ident, *label = NULL; int pin_id = -1; size_t defsize = 0; - u8 defbuf[32]; + u8 defbuf[0x100]; int r; path = file? &file->path : NULL; @@ -2398,7 +2401,7 @@ do_verify_pin(struct sc_profile *pro, struct sc_card *card, sc_file_t *file, unsigned int type, unsigned int reference) { size_t pinsize; - u8 pinbuf[32]; + u8 pinbuf[0x100]; pinsize = sizeof(pinbuf); return do_get_and_verify_secret(pro, card, file, type, reference, diff --git a/src/pkcs15init/pkcs15-oberthur.c b/src/pkcs15init/pkcs15-oberthur.c new file mode 100644 index 00000000..28ca0484 --- /dev/null +++ b/src/pkcs15init/pkcs15-oberthur.c @@ -0,0 +1,718 @@ +/* + * Oberthur specific operation for PKCS #15 initialization + * + * Copyright (C) 2002 Juha Yrjölä + * Copyright (C) 2003 Idealx + * Viktor Tarasov + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "pkcs15-init.h" +#include "profile.h" + +#include +#include +#include +#include + +#include "../libopensc/card-oberthur.h" + +#define COSM_TITLE "OberthurAWP" + +#define COSM_TLV_TAG 0x00 +#define COSM_LIST_TAG 0xFF +#define COSM_TAG_CONTAINER 0x0000 +#define COSM_TAG_CERT 0x0001 +#define COSM_TAG_PRVKEY_RSA 0x04B1 +#define COSM_TAG_PUBKEY_RSA 0x0349 +#define COSM_TAG_DES 0x0679 +#define COSM_TAG_DATA 0x0001 +#define COSM_IMPORTED 0x0000 +#define COSM_GENERATED 0x0004 + +#define TLV_TYPE_V 0 +#define TLV_TYPE_LV 1 +#define TLV_TYPE_TLV 2 + +// Should be greater then SC_PKCS15_TYPE_CLASS_MASK +#define SC_DEVICE_SPECIFIC_TYPE 0x1000 + +#define COSM_PUBLIC_LIST (SC_DEVICE_SPECIFIC_TYPE | 0x02) +#define COSM_PRIVATE_LIST (SC_DEVICE_SPECIFIC_TYPE | 0x03) +#define COSM_CONTAINER_LIST (SC_DEVICE_SPECIFIC_TYPE | 0x04) +#define COSM_TOKENINFO (SC_DEVICE_SPECIFIC_TYPE | 0x05) + +#define COSM_TYPE_PRKEY_RSA (SC_DEVICE_SPECIFIC_TYPE | SC_PKCS15_TYPE_PRKEY_RSA) +#define COSM_TYPE_PUBKEY_RSA (SC_DEVICE_SPECIFIC_TYPE | SC_PKCS15_TYPE_PUBKEY_RSA) + +static int cosm_update_pin(struct sc_profile *profile, struct sc_card *card, + struct sc_pkcs15_pin_info *info, const u8 *pin, size_t pin_len, + const u8 *puk, size_t puk_len); + +int cosm_delete_file(struct sc_card *card, struct sc_profile *profile, + struct sc_file *df); + +int cosm_delete_file(struct sc_card *card, struct sc_profile *profile, + struct sc_file *df) +{ + struct sc_path path; + struct sc_file *parent; + int rv = 0; + + sc_debug(card->ctx, " id %04X\n", df->id); + + if (df->type==SC_FILE_TYPE_DF) { + rv = sc_pkcs15init_authenticate(profile, card, df, SC_AC_OP_DELETE); + if (rv < 0) + goto done; + } + + /* Select the parent DF */ + + path = df->path; + path.len -= 2; + + rv = sc_select_file(card, &path, &parent); + if (rv < 0) + goto done; + + rv = sc_pkcs15init_authenticate(profile, card, parent, SC_AC_OP_DELETE); + sc_file_free(parent); + if (rv < 0) + goto done; + + memset(&path, 0, sizeof(path)); + path.type = SC_PATH_TYPE_FILE_ID; + path.value[0] = df->id >> 8; + path.value[1] = df->id & 0xFF; + path.len = 2; + + rv = sc_delete_file(card, &path); +done: + sc_debug(card->ctx, "return %i\n", rv); + return rv; +} + + +/* + * Erase the card + */ +static int cosm_erase_card(struct sc_profile *profile, struct sc_card *card) +{ + struct sc_file *df = profile->df_info->file, *dir; + int r; + + /* Delete EF(DIR). This may not be very nice + * against other applications that use this file, but + * extremely useful for testing :) + * Note we need to delete if before the DF because we create + * it *after* the DF. + * */ + card->ctx->suppress_errors++; + if (sc_profile_get_file(profile, "DIR", &dir) >= 0) { + sc_debug(card->ctx, "erase file dir %04X\n",dir->id); + r = cosm_delete_file(card, profile, dir); + sc_file_free(dir); + if (r < 0 && r != SC_ERROR_FILE_NOT_FOUND) + goto done; + } + + sc_debug(card->ctx, "erase file ddf %04X\n",df->id); + r=cosm_delete_file(card, profile, df); + + if (sc_profile_get_file(profile, "private-DF", &dir) >= 0) { + sc_debug(card->ctx, "erase file dir %04X\n",dir->id); + r = cosm_delete_file(card, profile, dir); + sc_file_free(dir); + if (r < 0 && r != SC_ERROR_FILE_NOT_FOUND) + goto done; + } + + if (sc_profile_get_file(profile, "public-DF", &dir) >= 0) { + sc_debug(card->ctx, "erase file dir %04X\n",dir->id); + r = cosm_delete_file(card, profile, dir); + sc_file_free(dir); + if (r < 0 && r != SC_ERROR_FILE_NOT_FOUND) + goto done; + } + + r = sc_profile_get_file(profile, COSM_TITLE"-AppDF", &dir); + if (!r) { + sc_debug(card->ctx, "delete %s; r %i\n", COSM_TITLE"-AppDF", r); + r = cosm_delete_file(card, profile, dir); + sc_file_free(dir); + } + +done: + sc_keycache_forget_key(NULL, -1, -1); + card->ctx->suppress_errors++; + + if (r == SC_ERROR_FILE_NOT_FOUND) + r=0; + + return r; +} + + +/* + * Initialize the Application DF + */ +static int +cosm_init_app(struct sc_profile *profile, struct sc_card *card, + struct sc_pkcs15_pin_info *pinfo, + const u8 *pin, size_t pin_len, + const u8 *puk, size_t puk_len) +{ + int r; + struct sc_file *file = NULL; + + sc_debug(card->ctx, "pin_len %i; puk_len %i\n", pin_len, puk_len); + /* Create the application DF */ + r = sc_pkcs15init_create_file(profile, card, profile->df_info->file); + if (r) + return r; + + /* Oberthur AWP file system is expected.*/ + /* Create private objects DF */ + if (sc_profile_get_file(profile, "private-DF", &file)) { + sc_error(card->ctx, "Inconsistent profile: cannot find private-DF"); + return SC_ERROR_INCONSISTENT_PROFILE; + } + + r = sc_pkcs15init_create_file(profile, card, file); + sc_file_free(file); + if (r) + return r; + + /* Create public objects DF */ + if (sc_profile_get_file(profile, "public-DF", &file)) { + sc_error(card->ctx, "Inconsistent profile: cannot find public-DF"); + return SC_ERROR_INCONSISTENT_PROFILE; + } + + r = sc_pkcs15init_create_file(profile, card, file); + sc_file_free(file); + if (r) + return r; + + /* Create Oberthur AWP application DF (5011), + * and populate with Oberthur's xxDF files*/ + r = sc_profile_get_file(profile, COSM_TITLE"-AppDF", &file); + sc_debug(card->ctx, "name %s; r %i\n", COSM_TITLE"-AppDF", r); + if (r==SC_ERROR_FILE_NOT_FOUND) { + sc_debug(card->ctx, "create file dir %04X\n", file->id); + r = sc_pkcs15init_create_file(profile, card, file); + sc_file_free(file); + } + if (r && r!=SC_ERROR_FILE_ALREADY_EXISTS) + return(r); + + sc_debug(card->ctx, "return OK\n"); + return 0; +} + +static int cosm_create_reference_data(struct sc_profile *profile, struct sc_card *card, + struct sc_pkcs15_pin_info *pinfo, + const u8 *pin, size_t pin_len, const u8 *puk, size_t puk_len ) +{ + int rv; + int puk_buff_len = 0; + unsigned char *puk_buff = NULL; + sc_pkcs15_pin_info_t profile_pin; + sc_pkcs15_pin_info_t profile_puk; + struct sc_cardctl_oberthur_createpin_info args; + + sc_debug(card->ctx, "pin lens %i/%i\n", pin_len, puk_len); + if (!pin || pin_len>0x40) + return SC_ERROR_INVALID_ARGUMENTS; + if (puk && !puk_len) + return SC_ERROR_INVALID_ARGUMENTS; + + rv = sc_select_file(card, &pinfo->path, NULL); + if (rv) + return rv; + + sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &profile_pin); + if (profile_pin.max_length > 0x100) + return SC_ERROR_INCONSISTENT_PROFILE; + + if (puk) { + int ii, jj; + unsigned char *ptr = (unsigned char *)puk; + + puk_buff = malloc(0x100); + if (!puk_buff) + goto done; + + sc_profile_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &profile_puk); + if (profile_puk.max_length > 0x100) + return SC_ERROR_INCONSISTENT_PROFILE; + memset(puk_buff, profile_puk.pad_char, 0x100); + for (ii=0; ii<8 && (ptr-puk) < puk_len && (*ptr); ii++) { + jj = 0; + while (isalnum(*ptr) && jj<16) { + *(puk_buff + ii*0x10 + jj++) = *ptr; + ++ptr; + } + while(!isalnum(*ptr) && (*ptr)) + ++ptr; + } + + puk_buff_len = ii*0x10; + } + + sc_debug(card->ctx, "pinfo->reference %i; tries %i\n", + pinfo->reference, profile_pin.tries_left); + + sc_debug(card->ctx, "sc_card_ctl %s\n","SC_CARDCTL_OBERTHUR_CREATE_PIN"); + args.type = SC_AC_CHV; + args.ref = pinfo->reference; + args.pin = (u8 *)pin; + args.pin_len = pin_len; + args.pin_tries = profile_pin.tries_left; + args.puk = puk_buff; + args.puk_len = puk_buff_len; + args.puk_tries = profile_puk.tries_left; + + if ((rv = sc_card_ctl(card, SC_CARDCTL_OBERTHUR_CREATE_PIN, &args)) < 0) + goto done; + +done: + if (puk_buff) + free(puk_buff); + + sc_debug(card->ctx, "return %i\n", rv); + return rv; +} + +/* + * Update PIN + */ +static int cosm_update_pin(struct sc_profile *profile, struct sc_card *card, + struct sc_pkcs15_pin_info *pinfo, const u8 *pin, size_t pin_len, + const u8 *puk, size_t puk_len ) +{ + int rv; + int tries_left = -1; + + sc_debug(card->ctx, "ref %i; flags %X\n", pinfo->reference, pinfo->flags); + + if (pinfo->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { + sc_error(card->ctx,"Pin references should be only in the profile" + "and in the card-oberthur.\n"); + if (pinfo->reference != 2) + return SC_ERROR_INVALID_PIN_REFERENCE; + + rv = sc_change_reference_data(card, SC_AC_CHV, pinfo->reference, puk, puk_len, + pin, pin_len, &tries_left); + sc_debug(card->ctx, "return value %X; tries left %i\n", rv, tries_left); + if (tries_left != -1) + sc_error(card->ctx, "Failed to change reference data for soPin: rv %X", rv); + + } + else { + rv = cosm_create_reference_data(profile, card, pinfo, + pin, pin_len, puk, puk_len); + } + + sc_debug(card->ctx, "return %i\n",rv); + return rv; +} + +static int +cosm_select_pin_reference(sc_profile_t *profile, sc_card_t *card, + sc_pkcs15_pin_info_t *pin_info) +{ + sc_file_t *pinfile; + + sc_debug(card->ctx, "ref %i; flags %X\n", pin_info->reference, pin_info->flags); + if (sc_profile_get_file(profile, COSM_TITLE "-AppDF", &pinfile) < 0) { + sc_error(card->ctx, "Profile doesn't define \"%s\"", COSM_TITLE "-AppDF"); + return SC_ERROR_INCONSISTENT_PROFILE; + } + + pin_info->path = pinfile->path; + sc_file_free(pinfile); + + if (pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { + pin_info->reference = 2; + } + + if (pin_info->reference < 0 || pin_info->reference > 3) + return SC_ERROR_INVALID_PIN_REFERENCE; + + if (!(pin_info->flags & SC_PKCS15_PIN_FLAG_SO_PIN)) { + if (pin_info->reference == 2) + return SC_ERROR_INVALID_PIN_REFERENCE; + else if (pin_info->reference == 0) + pin_info->reference = 1; + } + sc_debug(card->ctx, "return %i\n",0); + return 0; +} + +/* + * Store a PIN + */ +static int +cosm_create_pin(sc_profile_t *profile, sc_card_t *card, sc_file_t *df, + sc_pkcs15_object_t *pin_obj, + const unsigned char *pin, size_t pin_len, + const unsigned char *puk, size_t puk_len) +{ + sc_pkcs15_pin_info_t *pinfo = (sc_pkcs15_pin_info_t *) pin_obj->data; + sc_file_t *pinfile; + int r = 0, type; + + sc_debug(card->ctx, "ref %i; flags %X\n", pinfo->reference, pinfo->flags); + if (sc_profile_get_file(profile, COSM_TITLE "-AppDF", &pinfile) < 0) { + sc_error(card->ctx, "Profile doesn't define \"%s\"", COSM_TITLE "-AppDF"); + return SC_ERROR_INCONSISTENT_PROFILE; + } + + pinfo->path = pinfile->path; + sc_file_free(pinfile); + + if (pinfo->flags & SC_PKCS15_PIN_FLAG_SO_PIN) { + type = SC_PKCS15INIT_SO_PIN; + + if (pinfo->reference != 2) + return SC_ERROR_INVALID_ARGUMENTS; + } + else { + type = SC_PKCS15INIT_USER_PIN; + + if (pinfo->reference !=1) + return SC_ERROR_INVALID_PIN_REFERENCE; + } + + if (pin && pin_len) { + r = cosm_update_pin(profile, card, pinfo, pin, pin_len, puk, puk_len); + } + else { + sc_debug(card->ctx, "User PIN not updated"); + } + sc_debug(card->ctx, "return %i\n",r); + + sc_keycache_set_pin_name(&pinfo->path, pinfo->reference, type); + pinfo->flags &= ~SC_PKCS15_PIN_FLAG_LOCAL; + return r; +} + + +/* + * Allocate a file + */ +static int +cosm_new_file(struct sc_profile *profile, struct sc_card *card, + unsigned int type, unsigned int num, struct sc_file **out) +{ + struct sc_file *file; + char *template = NULL, *desc = NULL; + unsigned int structure = 0xFFFFFFFF; + + sc_debug(card->ctx, "type %X; num %i\n",type, num); + while (1) { + switch (type) { + case SC_PKCS15_TYPE_PRKEY_RSA: + case COSM_TYPE_PRKEY_RSA: + desc = "RSA private key"; + template = "template-private-key"; + structure = SC_CARDCTL_OBERTHUR_KEY_RSA_CRT; + break; + case SC_PKCS15_TYPE_PUBKEY_RSA: + case COSM_TYPE_PUBKEY_RSA: + desc = "RSA public key"; + template = "template-public-key"; + structure = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; + break; + case SC_PKCS15_TYPE_PUBKEY_DSA: + desc = "DSA public key"; + template = "template-public-key"; + break; + case SC_PKCS15_TYPE_PRKEY: + desc = "extractable private key"; + template = "template-extractable-key"; + break; + case SC_PKCS15_TYPE_CERT: + desc = "certificate"; + template = "template-certificate"; + break; + case SC_PKCS15_TYPE_DATA_OBJECT: + desc = "data object"; + template = "template-public-data"; + break; + } + if (template) + break; + /* If this is a specific type such as + * SC_PKCS15_TYPE_CERT_FOOBAR, fall back to + * the generic class (SC_PKCS15_TYPE_CERT) + */ + if (!(type & ~SC_PKCS15_TYPE_CLASS_MASK)) { + sc_error(card->ctx, "File type %X not supported by card driver", + type); + return SC_ERROR_INVALID_ARGUMENTS; + } + type &= SC_PKCS15_TYPE_CLASS_MASK; + } + + sc_debug(card->ctx, "template %s; num %i\n",template, num); + if (sc_profile_get_file(profile, template, &file) < 0) { + sc_error(card->ctx, "Profile doesn't define %s template '%s'\n", + desc, template); + return SC_ERROR_NOT_SUPPORTED; + } + + file->id |= (num & 0xFF); + file->path.value[file->path.len-1] |= (num & 0xFF); + if (file->type == SC_FILE_TYPE_INTERNAL_EF) { + file->ef_structure = structure; + } + + sc_debug(card->ctx, "file size %i; ef type %i/%i; id %04X\n",file->size, + file->type, file->ef_structure, file->id); + *out = file; + return 0; +} + + +/* + * RSA key generation + */ +static int +cosm_old_generate_key(struct sc_profile *profile, struct sc_card *card, + unsigned int index, unsigned int keybits, + sc_pkcs15_pubkey_t *pubkey, + struct sc_pkcs15_prkey_info *info) +{ + struct sc_cardctl_oberthur_genkey_info args; + struct sc_file *prkf = NULL, *tmpf = NULL; + sc_path_t path; + int rv; + + sc_debug(card->ctx, "index %i; nn %i\n",index,keybits); + if (keybits < 512 || keybits > 2048 || (keybits%0x20)) { + sc_error(card->ctx, "Unsupported key size %u\n", keybits); + return SC_ERROR_INVALID_ARGUMENTS; + } + + /* Get private key file from profile. */ + if ((rv = cosm_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, index, + &prkf)) < 0) + goto failed; + sc_debug(card->ctx, "prv ef type %i\n",prkf->ef_structure); + prkf->size = keybits; + + /* Access condition of private object DF. */ + path = prkf->path; + path.len -= 2; + if ((rv = sc_profile_get_file_by_path(profile, &path, &tmpf))) + goto failed; + else if ((rv = sc_pkcs15init_authenticate(profile, card, tmpf, + SC_AC_OP_CRYPTO)) < 0) + goto failed; + else if ((rv = sc_pkcs15init_authenticate(profile, card, tmpf, + SC_AC_OP_CREATE)) < 0) + goto failed; + else + sc_file_free(tmpf); + + /* In the private key DF create the temporary public RSA file. */ + sc_debug(card->ctx, "ready to create public key\n"); + sc_file_dup(&tmpf, prkf); + tmpf->type = SC_FILE_TYPE_INTERNAL_EF; + tmpf->ef_structure = SC_CARDCTL_OBERTHUR_KEY_RSA_PUBLIC; + tmpf->id = 0x1012; + tmpf->path.value[tmpf->path.len - 2] = 0x10; + tmpf->path.value[tmpf->path.len - 1] = 0x12; + + if ((rv = sc_pkcs15init_create_file(profile, card, prkf))) + goto failed; + else if ((rv = sc_pkcs15init_create_file(profile, card, tmpf))) + goto failed; + + memset(&args, 0, sizeof(args)); + args.id_prv = prkf->id; + args.id_pub = tmpf->id; + args.exponent = 0x10001; + args.key_bits = keybits; + args.pubkey_len = keybits/8; + args.pubkey = malloc(keybits/8); + if (!args.pubkey) { + rv = SC_ERROR_OUT_OF_MEMORY; + goto failed; + } + + sc_debug(card->ctx, "sc_card_ctl %s\n","SC_CARDCTL_OBERTHUR_GENERATE_KEY"); + if ((rv = sc_card_ctl(card, SC_CARDCTL_OBERTHUR_GENERATE_KEY, &args)) < 0) + goto failed; + + /* extract public key */ + pubkey->algorithm = SC_ALGORITHM_RSA; + pubkey->u.rsa.modulus.len = keybits / 8; + pubkey->u.rsa.modulus.data = (u8 *) malloc(keybits / 8); + if (!pubkey->u.rsa.modulus.data) { + rv = SC_ERROR_MEMORY_FAILURE; + goto failed; + } + + //FIXME and if the exponent length is not 3? + pubkey->u.rsa.exponent.len = 3; + pubkey->u.rsa.exponent.data = (u8 *) malloc(3); + if (!pubkey->u.rsa.exponent.data) { + rv = SC_ERROR_MEMORY_FAILURE; + goto failed; + } + memcpy(pubkey->u.rsa.exponent.data, "\x01\x00\x01", 3); + memcpy(pubkey->u.rsa.modulus.data, args.pubkey, args.pubkey_len); + + info->key_reference = 1; + info->path = prkf->path; + + sc_debug(card->ctx, "delete temporary public key\n"); + if ((rv = cosm_delete_file(card, profile, tmpf))) + goto failed; + +failed: + if (tmpf) sc_file_free(tmpf); + if (prkf) sc_file_free(prkf); + sc_debug(card->ctx, "return %i\n",rv); + return rv; +} + + +/* + * Store a private key + */ +static int +cosm_new_key(struct sc_profile *profile, struct sc_card *card, + struct sc_pkcs15_prkey *key, unsigned int index, + struct sc_pkcs15_prkey_info *info) +{ + struct sc_file *prvfile = NULL, *pubfile = NULL; + struct sc_pkcs15_prkey_rsa *rsa = NULL; + struct sc_pkcs15_bignum bn[6]; + u8 *buff; + int rv, ii; + + sc_debug(card->ctx, " index %i\n", index); + if (key->algorithm != SC_ALGORITHM_RSA) { + sc_error(card->ctx, "For a while supports only RSA keys."); + return SC_ERROR_NOT_SUPPORTED; + } + + /* Create and populate the private part. */ + rv = cosm_new_file(profile, card, SC_PKCS15_TYPE_PRKEY_RSA, index, + &prvfile); + if (rv < 0) + return SC_ERROR_SYNTAX_ERROR; + + sc_debug(card->ctx, " prvfile->id %i; path=%s\n", + prvfile->id, sc_print_path(&prvfile->path)); + + rsa = &key->u.rsa; + + prvfile->size = rsa->modulus.len << 3; + buff = malloc(rsa->modulus.len); + if (!buff) { + sc_error(card->ctx, "Memory allocation error."); + return SC_ERROR_OUT_OF_MEMORY; + } + + rv = sc_select_file(card, &prvfile->path, NULL); + if (rv==SC_ERROR_FILE_NOT_FOUND) + rv = sc_pkcs15init_create_file(profile, card, prvfile); + + if (rv <0) + goto failed; + + if ((rv = sc_pkcs15init_authenticate(profile, card, prvfile, + SC_AC_OP_UPDATE)) <0) + goto failed; + + bn[0] = rsa->p; + bn[1] = rsa->q; + bn[2] = rsa->iqmp; + bn[3] = rsa->dmp1; + bn[4] = rsa->dmq1; + for (ii=0;ii<5;ii++) { + struct sc_cardctl_oberthur_updatekey_info args; + + memset(&args, 0, sizeof(args)); + args.type = SC_CARDCTL_OBERTHUR_KEY_RSA_CRT; + args.component = ii+1; + args.data = bn[ii].data; + args.len = bn[ii].len; + rv = sc_card_ctl(card, SC_CARDCTL_OBERTHUR_UPDATE_KEY, &args); + if (rv) + goto failed; + } + + info->path = prvfile->path; + info->modulus_length = rsa->modulus.len << 3; + +failed: + if (pubfile) sc_file_free(pubfile); + if (prvfile) sc_file_free(prvfile); + if (buff) free(buff); + + sc_debug(card->ctx, "return %i\n", rv); + return rv; +} + + +struct sc_pkcs15init_operations sc_pkcs15init_oberthur_operations; + +struct sc_pkcs15init_operations * +sc_pkcs15init_get_oberthur_ops(void) +{ + struct sc_pkcs15init_operations *ops = &sc_pkcs15init_oberthur_operations; + + ops->erase_card = cosm_erase_card; + +//NEW + ops->create_dir = NULL; + ops->create_domain = NULL; + ops->select_pin_reference = cosm_select_pin_reference; + ops->create_pin = cosm_create_pin; + ops->select_key_reference = NULL; + ops->create_key = NULL; + ops->store_key = NULL; + ops->generate_key = NULL; + ops->encode_private_key = NULL; + ops->encode_public_key = NULL; + +//OLD + ops->init_app = cosm_init_app; + ops->new_pin = NULL; + ops->new_key = cosm_new_key; + ops->new_file = cosm_new_file; + ops->old_generate_key = cosm_old_generate_key; + + return ops; +}; diff --git a/src/pkcs15init/pkcs15.profile b/src/pkcs15init/pkcs15.profile index 5a482896..64e6c6fa 100644 --- a/src/pkcs15init/pkcs15.profile +++ b/src/pkcs15init/pkcs15.profile @@ -72,6 +72,17 @@ option small { } } +option oberthur { + macros { + odf-size = 512; + aodf-size = 512; + cdf-size = 3072; + prkdf-size = 1024; + pukdf-size = 1024; + dodf-size = 512; + } +} + # This option tells pkcs15-init to use the direct option # when storing certificates on the card (i.e. put the # certificates into the CDF itself, rather than a