diff --git a/src/libopensc/card-iasecc.c b/src/libopensc/card-iasecc.c new file mode 100644 index 00000000..445acd14 --- /dev/null +++ b/src/libopensc/card-iasecc.c @@ -0,0 +1,3075 @@ +/* + * card-iasecc.c: Support for IAS/ECC smart cards + * + * Copyright (C) 2010 Viktor Tarasov + * OpenTrust + * + * 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 + +#ifdef ENABLE_OPENSSL /* empty file without openssl */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" +#include "asn1.h" +#include "cardctl.h" +#include "opensc.h" +/* #include "sm.h" */ +#include "pkcs15.h" +/* #include "hash-strings.h" */ + +#include "iasecc.h" + +#define ALLOW_IGNORE_EXTERNAL_AUTHENTICATION + +#define IASECC_CARD_DEFAULT_FLAGS (SC_CARD_FLAG_RNG \ + | SC_ALGORITHM_ONBOARD_KEY_GEN \ + | SC_ALGORITHM_RSA_PAD_ISO9796 \ + | SC_ALGORITHM_RSA_PAD_PKCS1 \ + | SC_ALGORITHM_RSA_HASH_NONE \ + | SC_ALGORITHM_RSA_HASH_SHA1 \ + | SC_ALGORITHM_RSA_HASH_SHA256) + +/* generic iso 7816 operations table */ +static const struct sc_card_operations *iso_ops = NULL; + +/* our operations table with overrides */ +static struct sc_card_operations iasecc_ops; + +static struct sc_card_driver iasecc_drv = { + "IAS-ECC", + "iasecc", + &iasecc_ops, + NULL, 0, NULL +}; + +static struct sc_atr_table iasecc_known_atrs[] = { + { "3B:7F:96:00:00:00:31:B8:64:40:70:14:10:73:94:01:80:82:90:00", NULL, + "IAS/ECC Gemalto", SC_CARD_TYPE_IASECC_GEMALTO, 0, NULL }, + { "3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:08:00:07:90:00:FE", NULL, + "IAS/ECC v1.0.1 Oberthur", SC_CARD_TYPE_IASECC_OBERTHUR, 0, NULL }, + { "3B:7D:13:00:00:4D:44:57:2D:49:41:53:2D:43:41:52:44:32", NULL, + "IAS/ECC v1.0.1 Sagem MDW-IAS-CARD2", SC_CARD_TYPE_IASECC_SAGEM, 0, NULL }, + { "3B:7F:18:00:00:00:31:B8:64:50:23:EC:C1:73:94:01:80:82:90:00", NULL, + "IAS/ECC v1.0.1 Sagem ypsID S3", SC_CARD_TYPE_IASECC_SAGEM, 0, NULL }, + { NULL, NULL, NULL, 0, 0, NULL } +}; + +static struct sc_aid GlobalPlatform_CardManager_AID = { + { 0xA0,0x00,0x00,0x00,0x03,0x00,0x00}, 7 +}; +static struct sc_aid GlobalPlatform_ISD_Default_RID = { + { 0xA0,0x00,0x00,0x01,0x51,0x00,0x00}, 7 +}; +static struct sc_aid OberthurIASECC_AID = { + {0xA0,0x00,0x00,0x00,0x77,0x01,0x08,0x00,0x07,0x00,0x00,0xFE,0x00,0x00,0x01,0x00}, 16 +}; + +struct iasecc_pin_status { + unsigned char sha1[SHA_DIGEST_LENGTH]; + unsigned char reference; + + struct iasecc_pin_status *next; + struct iasecc_pin_status *prev; +}; + +struct iasecc_pin_status *checked_pins = NULL; + +static int iasecc_select_file(struct sc_card *card, const struct sc_path *path, struct sc_file **file_out); +static int iasecc_process_fci(struct sc_card *card, struct sc_file *file, const unsigned char *buf, size_t buflen); +static int iasecc_get_serialnr(struct sc_card *card, struct sc_serial_number *serial); +static int iasecc_sdo_get_data(struct sc_card *card, struct iasecc_sdo *sdo); +static int iasecc_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data); +static int iasecc_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, int *tries_left); +static int iasecc_get_free_reference(struct sc_card *card, struct iasecc_ctl_get_free_reference *ctl_data); + +static int +iasecc_chv_cache_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_pin_status *pin_status = NULL, *current = NULL; + + LOG_FUNC_CALLED(ctx); + + for(current = checked_pins; current; current = current->next) + if (current->reference == pin_cmd->pin_reference) + break; + + if (current) { + sc_log(ctx, "iasecc_chv_cache_verified() current PIN-%i", current->reference); + pin_status = current; + } + else { + pin_status = calloc(1, sizeof(struct iasecc_pin_status)); + if (!pin_status) + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot callocate PIN status info"); + sc_log(ctx, "iasecc_chv_cache_verified() allocated %p", pin_status); + } + + pin_status->reference = pin_cmd->pin_reference; + if (pin_cmd->pin1.data) + SHA1(pin_cmd->pin1.data, pin_cmd->pin1.len, pin_status->sha1); + else + memset(pin_status->sha1, 0, SHA_DIGEST_LENGTH); + + sc_log(ctx, "iasecc_chv_cache_verified() sha1(PIN): %s", sc_dump_hex(pin_status->sha1, SHA_DIGEST_LENGTH)); + + if (!current) { + if (!checked_pins) { + checked_pins = pin_status; + } + else { + checked_pins->prev = pin_status; + pin_status->next = checked_pins; + checked_pins = pin_status; + } + } + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_chv_cache_clean(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_pin_status *current = NULL; + + LOG_FUNC_CALLED(ctx); + + for(current = checked_pins; current; current = current->next) + if (current->reference == pin_cmd->pin_reference) + break; + + if (!current) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + + + if (current->next && current->prev) { + current->prev->next = current->next; + current->next->prev = current->prev; + } + else if (!current->prev) { + checked_pins = current->next; + } + else if (!current->next && current->prev) { + current->prev->next = NULL; + } + + free(current); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static struct iasecc_pin_status * +iasecc_chv_cache_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_pin_status *current = NULL; + unsigned char data_sha1[SHA_DIGEST_LENGTH]; + + LOG_FUNC_CALLED(ctx); + + if (pin_cmd->pin1.data) + SHA1(pin_cmd->pin1.data, pin_cmd->pin1.len, data_sha1); + else + memset(data_sha1, 0, SHA_DIGEST_LENGTH); + sc_log(ctx, "data_sha1: %s", sc_dump_hex(data_sha1, SHA_DIGEST_LENGTH)); + + for(current = checked_pins; current; current = current->next) + if (current->reference == pin_cmd->pin_reference) + break; + + if (current && !memcmp(data_sha1, current->sha1, SHA_DIGEST_LENGTH)) { + sc_log(ctx, "PIN-%i status 'verified'", pin_cmd->pin_reference); + return current; + } + + sc_log(ctx, "PIN-%i status 'not verified'", pin_cmd->pin_reference); + return NULL; +} + + +static int +iasecc_select_mf(struct sc_card *card, struct sc_file **file_out) +{ + struct sc_context *ctx = card->ctx; + struct sc_path path; + int rv; + + LOG_FUNC_CALLED(ctx); + + if (file_out) + *file_out = NULL; + + memset(&path, 0, sizeof(struct sc_path)); + if (!card->ef_atr || !card->ef_atr->aid.len) { + sc_format_path("3F00", &path); + path.type = SC_PATH_TYPE_FILE_ID; + rv = iso_ops->select_file(card, &path, file_out); + } + else { + path.type = SC_PATH_TYPE_DF_NAME; + memcpy(path.value, card->ef_atr->aid.value, card->ef_atr->aid.len); + path.len = card->ef_atr->aid.len; + rv = iasecc_select_file(card, &path, file_out); + LOG_TEST_RET(ctx, rv, "Unable to ROOT selection"); + + /* When selecting Root DF Oberthur's IAS/ECC card do not returns FCI data */ + if (file_out && *file_out == NULL) { + struct sc_file *mf_file = sc_file_new(); + if (mf_file == NULL) + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot allocate MF file"); + mf_file->type = SC_FILE_TYPE_DF; + mf_file->path = path; + + *file_out = mf_file; + } + } + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_select_aid(struct sc_card *card, struct sc_aid *aid, unsigned char *out, size_t *out_len) +{ + struct sc_apdu apdu; + unsigned char apdu_resp[SC_MAX_APDU_BUFFER_SIZE]; + int rv; + + /* Select Card Manager (to deselect previously selected application) */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x04, 0x00); + apdu.lc = aid->len; + apdu.data = aid->value; + apdu.datalen = aid->len; + apdu.resplen = sizeof(apdu_resp); + apdu.resp = apdu_resp; + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(card->ctx, rv, "Cannot select AID"); + + if (*out_len < apdu.resplen) + LOG_TEST_RET(card->ctx, SC_ERROR_BUFFER_TOO_SMALL, "Cannot select AID"); + memcpy(out, apdu.resp, apdu.resplen); + + return SC_SUCCESS; +} + + +static int +iasecc_match_card(struct sc_card *card) +{ + struct sc_context *ctx = card->ctx; + int i; + + sc_log(ctx, "iasecc_match_card(%s) called", sc_dump_hex(card->atr.value, card->atr.len)); + i = _sc_match_atr(card, iasecc_known_atrs, &card->type); + if (i < 0) { + sc_log(ctx, "card not matched"); + return 0; + } + + sc_log(ctx, "'%s' card matched", iasecc_known_atrs[i].name); + return 1; +} + + +static int iasecc_parse_ef_atr(struct sc_card *card) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_private_data *pdata = (struct iasecc_private_data *) card->drv_data; + struct iasecc_version *version = &pdata->version; + struct iasecc_io_buffer_sizes *sizes = &pdata->max_sizes; + int rv; + + LOG_FUNC_CALLED(ctx); + rv = sc_parse_ef_atr(card); + LOG_TEST_RET(ctx, rv, "MF selection error"); + + if (card->ef_atr->pre_issuing_len < 4) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid pre-issuing data"); + + version->ic_manufacturer = card->ef_atr->pre_issuing[0]; + version->ic_type = card->ef_atr->pre_issuing[1]; + version->os_version = card->ef_atr->pre_issuing[2]; + version->iasecc_version = card->ef_atr->pre_issuing[3]; + sc_log(ctx, "EF.ATR: IC manufacturer/type %X/%X, OS/IasEcc versions %X/%X", + version->ic_manufacturer, version->ic_type, version->os_version, version->iasecc_version); + + if (card->ef_atr->issuer_data_len < 16) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid issuer data"); + + sizes->send = card->ef_atr->issuer_data[2] * 0x100 + card->ef_atr->issuer_data[3]; + sizes->send_sc = card->ef_atr->issuer_data[6] * 0x100 + card->ef_atr->issuer_data[7]; + sizes->recv = card->ef_atr->issuer_data[10] * 0x100 + card->ef_atr->issuer_data[11]; + sizes->recv_sc = card->ef_atr->issuer_data[14] * 0x100 + card->ef_atr->issuer_data[15]; + + card->max_send_size = sizes->send; + card->max_recv_size = sizes->recv; + sc_log(ctx, "EF.ATR: max send/recv sizes %X/%X", card->max_send_size, card->max_recv_size); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_init_gemalto(struct sc_card *card) +{ + struct sc_context *ctx = card->ctx; + struct sc_path path; + unsigned int flags; + int rv = 0; + + LOG_FUNC_CALLED(ctx); + + flags = IASECC_CARD_DEFAULT_FLAGS; + + _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); + _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); + + card->caps = SC_CARD_CAP_RNG; + card->caps |= SC_CARD_CAP_RSA_2048; + card->caps |= SC_CARD_CAP_APDU_EXT; + card->caps |= SC_CARD_CAP_USE_FCI_AC; + + sc_format_path("3F00", &path); + sc_select_file(card, &path, NULL); + + rv = iasecc_parse_ef_atr(card); + sc_log(ctx, "rv %i", rv); + if (rv == SC_ERROR_FILE_NOT_FOUND) { + sc_log(ctx, "Select MF"); + rv = iasecc_select_mf(card, NULL); + sc_log(ctx, "rv %i", rv); + LOG_TEST_RET(ctx, rv, "MF selection error"); + + rv = iasecc_parse_ef_atr(card); + sc_log(ctx, "rv %i", rv); + } + sc_log(ctx, "rv %i", rv); + LOG_TEST_RET(ctx, rv, "Cannot read/parse EF.ATR"); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_oberthur_match(struct sc_card *card) +{ + struct sc_context *ctx = card->ctx; + unsigned char *hist = card->reader->atr_info.hist_bytes; + + LOG_FUNC_CALLED(ctx); + + if (*hist != 0x80 || ((*(hist+1)&0xF0) != 0xF0)) + LOG_FUNC_RETURN(ctx, SC_ERROR_OBJECT_NOT_FOUND); + + sc_log(ctx, "AID in historical_bytes '%s'", sc_dump_hex(hist + 2, *(hist+1) & 0x0F)); + + if (memcmp(hist + 2, OberthurIASECC_AID.value, *(hist+1) & 0x0F)) + LOG_FUNC_RETURN(ctx, SC_ERROR_RECORD_NOT_FOUND); + + if (!card->ef_atr) + card->ef_atr = calloc(1, sizeof(struct sc_ef_atr)); + if (!card->ef_atr) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + + memcpy(card->ef_atr->aid.value, OberthurIASECC_AID.value, OberthurIASECC_AID.len); + card->ef_atr->aid.len = OberthurIASECC_AID.len; + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_init_oberthur(struct sc_card *card) +{ + struct sc_context *ctx = card->ctx; + unsigned char resp[0x100]; + size_t resp_len; + unsigned int flags; + int rv = 0; + + LOG_FUNC_CALLED(ctx); + + flags = IASECC_CARD_DEFAULT_FLAGS; + + _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); + _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); + + card->caps = SC_CARD_CAP_RNG; + card->caps |= SC_CARD_CAP_RSA_2048; + card->caps |= SC_CARD_CAP_APDU_EXT; + card->caps |= SC_CARD_CAP_USE_FCI_AC; + + iasecc_parse_ef_atr(card); + + resp_len = sizeof(resp); + if (iasecc_select_aid(card, &GlobalPlatform_CardManager_AID, resp, &resp_len)) { + resp_len = sizeof(resp); + iasecc_select_aid(card, &GlobalPlatform_ISD_Default_RID, resp, &resp_len); + } + + rv = iasecc_oberthur_match(card); + LOG_TEST_RET(ctx, rv, "unknown Oberthur's IAS/ECC card"); + + rv = iasecc_select_mf(card, NULL); + LOG_TEST_RET(ctx, rv, "MF selection error"); + + rv = iasecc_parse_ef_atr(card); + LOG_TEST_RET(ctx, rv, "EF.ATR read or parse error"); + + sc_log(ctx, "EF.ATR(aid:'%s')", sc_dump_hex(card->ef_atr->aid.value, card->ef_atr->aid.len)); + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_init_sagem(struct sc_card *card) +{ + struct sc_context *ctx = card->ctx; + unsigned int flags; + int rv = 0; + + LOG_FUNC_CALLED(ctx); + + flags = IASECC_CARD_DEFAULT_FLAGS; + + _sc_card_add_rsa_alg(card, 1024, flags, 0x10001); + _sc_card_add_rsa_alg(card, 2048, flags, 0x10001); + + card->caps = SC_CARD_CAP_RNG; + card->caps |= SC_CARD_CAP_RSA_2048; + card->caps |= SC_CARD_CAP_APDU_EXT; + card->caps |= SC_CARD_CAP_USE_FCI_AC; + + rv = iasecc_parse_ef_atr(card); + if (rv == SC_ERROR_FILE_NOT_FOUND) { + rv = iasecc_select_mf(card, NULL); + LOG_TEST_RET(ctx, rv, "MF selection error"); + + rv = iasecc_parse_ef_atr(card); + } + LOG_TEST_RET(ctx, rv, "ECC: ATR parse failed"); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_init(struct sc_card *card) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_private_data *private_data = NULL; + int ii, rv = SC_ERROR_NO_CARD_SUPPORT; + + LOG_FUNC_CALLED(ctx); + private_data = (struct iasecc_private_data *) calloc(1, sizeof(struct iasecc_private_data)); + if (private_data == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + + for(ii=0;iasecc_known_atrs[ii].atr;ii++) { + if (card->type == iasecc_known_atrs[ii].type) { + card->name = iasecc_known_atrs[ii].name; + card->flags = iasecc_known_atrs[ii].flags; + break; + } + } + + if (!iasecc_known_atrs[ii].atr) + LOG_FUNC_RETURN(ctx, SC_ERROR_NO_CARD_SUPPORT); + + card->cla = 0x00; + card->drv_data = private_data; + + if (card->type == SC_CARD_TYPE_IASECC_GEMALTO) + rv = iasecc_init_gemalto(card); + else if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR) + rv = iasecc_init_oberthur(card); + else if (card->type == SC_CARD_TYPE_IASECC_SAGEM) + rv = iasecc_init_sagem(card); + + if (!rv) { + if (card->ef_atr && card->ef_atr->aid.len) { + struct sc_path path; + + memset(&path, 0, sizeof(struct sc_path)); + path.type = SC_PATH_TYPE_DF_NAME; + memcpy(path.value, card->ef_atr->aid.value, card->ef_atr->aid.len); + path.len = card->ef_atr->aid.len; + + rv = iasecc_select_file(card, &path, NULL); + sc_log(ctx, "Select ECC ROOT with the AID from EF.ATR: rv %i", rv); + LOG_TEST_RET(ctx, rv, "Select EF.ATR AID failed"); + } + + rv = iasecc_get_serialnr(card, NULL); + } + + sc_log(ctx, "EF.ATR(aid:'%s')", sc_dump_hex(card->ef_atr->aid.value, card->ef_atr->aid.len)); + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_read_binary(struct sc_card *card, unsigned int offs, + unsigned char *buf, size_t count, unsigned long flags) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "iasecc_read_binary(card:%p) offs %i; count %i", card, offs, count); + if (offs > 0x7fff) { + sc_log(ctx, "invalid EF offset: 0x%X > 0x7FFF", offs); + return SC_ERROR_OFFSET_TOO_LARGE; + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, (offs >> 8) & 0x7F, offs & 0xFF); + apdu.le = count < 0x100 ? count : 0x100; + apdu.resplen = count; + apdu.resp = buf; + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "iasecc_read_binary() failed"); + //if (apdu.resplen == 0) + // SC_FUNC_RETURN(ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2)); + + sc_log(ctx, "iasecc_read_binary() apdu.resplen %i", apdu.resplen); + + if (apdu.resplen == IASECC_READ_BINARY_LENGTH_MAX && apdu.resplen < count) { + rv = iasecc_read_binary(card, offs + apdu.resplen, buf + apdu.resplen, count - apdu.resplen, flags); + if (rv != SC_ERROR_WRONG_LENGTH) { + LOG_TEST_RET(ctx, rv, "iasecc_read_binary() read tail failed"); + apdu.resplen += rv; + } + } + + LOG_FUNC_RETURN(ctx, apdu.resplen); +} + + +static int +iasecc_erase_binary(struct sc_card *card, unsigned int offs, size_t count, unsigned long flags) +{ + struct sc_context *ctx = card->ctx; + const struct sc_acl_entry *entry = NULL; + unsigned char buf_zero[0x400]; + size_t sz; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "iasecc_erase_binary(card:%p) count %i", card, count); + if (!count) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "'ERASE BINARY' with ZERO count not supported"); + + sc_print_cache(card); + + if (card->cache.valid && card->cache.current_ef) { + entry = sc_file_get_acl_entry(card->cache.current_ef, SC_AC_OP_UPDATE); + sc_log(ctx, "UPDATE method/reference %X/%X", entry->method, entry->key_ref); + + if (entry->method == SC_AC_SCB && (entry->key_ref & IASECC_SCB_METHOD_SM)) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); + } + + + memset(buf_zero, 0, sizeof(buf_zero)); + while (count) { + sc_log(ctx, "count %i, max_send_size %i", count, card->max_send_size); + sz = count > card->max_send_size ? card->max_send_size : count; + + rv = iso_ops->update_binary(card, offs, buf_zero, sz, flags); + LOG_TEST_RET(ctx, rv, "write empty buffer failed"); + + offs += sz; + count -= sz; + } + + rv = SC_SUCCESS; + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_select_file(struct sc_card *card, const struct sc_path *path, + struct sc_file **file_out) +{ + struct sc_context *ctx = card->ctx; + struct sc_path lpath; + int rv, ii; + + LOG_FUNC_CALLED(ctx); + memcpy(&lpath, path, sizeof(struct sc_path)); + + sc_log(ctx, "iasecc_select_file(card:%p) path.len %i; path.type %i; aid_len %i", + card, path->len, path->type, path->aid.len); + sc_log(ctx, "iasecc_select_file() path:%s", sc_print_path(path)); + + sc_print_cache(card); + if (lpath.len >= 2 && lpath.value[0] == 0x3F && lpath.value[1] == 0x00) { + struct sc_path mfpath; + int rv; + + memset(&mfpath, 0, sizeof(struct sc_path)); + sc_log(ctx, "EF.ATR(aid:'%s')", card->ef_atr ? sc_dump_hex(card->ef_atr->aid.value, card->ef_atr->aid.len) : ""); + + rv = iasecc_select_mf(card, file_out); + LOG_TEST_RET(ctx, rv, "MF selection error"); + + if (lpath.len >= 2 && lpath.value[0] == 0x3F && lpath.value[1] == 0x00) { + memcpy(&lpath.value[0], &lpath.value[2], lpath.len - 2); + lpath.len -= 2; + } + } + + if (lpath.aid.len) { + struct sc_file *file = NULL; + struct sc_path ppath; + + sc_log(ctx, "iasecc_select_file() select parent AID:%p/%i", lpath.aid.value, lpath.aid.len); + sc_log(ctx, "iasecc_select_file() select parent AID:%s", sc_dump_hex(lpath.aid.value, lpath.aid.len)); + memset(&ppath, 0, sizeof(ppath)); + memcpy(ppath.value, lpath.aid.value, lpath.aid.len); + ppath.len = lpath.aid.len; + ppath.type = SC_PATH_TYPE_DF_NAME; + + rv = iasecc_select_file(card, &ppath, &file); + LOG_TEST_RET(ctx, rv, "select AID path failed"); + + if (file_out) + *file_out = file; + else if (file) + sc_file_free(file); + + if (lpath.type == SC_PATH_TYPE_DF_NAME) + lpath.type = SC_PATH_TYPE_FROM_CURRENT; + } + + if (lpath.type == SC_PATH_TYPE_PATH) + lpath.type = SC_PATH_TYPE_FROM_CURRENT; + + if (!lpath.len) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + + sc_print_cache(card); + + if (card->cache.valid && card->cache.current_df && lpath.type == SC_PATH_TYPE_DF_NAME + && card->cache.current_df->path.len == lpath.len + && !memcmp(card->cache.current_df->path.value, lpath.value, lpath.len)) { + sc_log(ctx, "returns current DF path %s", sc_print_path(&card->cache.current_df->path)); + if (file_out) + sc_file_dup(file_out, card->cache.current_df); + } + else { + struct sc_apdu apdu; + struct sc_file *file = NULL; + unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; + int pathlen = lpath.len; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00); + + if (card->type != SC_CARD_TYPE_IASECC_GEMALTO + && card->type != SC_CARD_TYPE_IASECC_OBERTHUR + && card->type != SC_CARD_TYPE_IASECC_SAGEM) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported card"); + + if (lpath.type == SC_PATH_TYPE_FILE_ID) { + apdu.p1 = 0x02; + if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR) { + apdu.p1 = 0x01; + apdu.p2 = 0x04; + } + } + else if (lpath.type == SC_PATH_TYPE_FROM_CURRENT) { + apdu.p1 = 0x09; + if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR) + apdu.p2 = 0x04; + } + else if (lpath.type == SC_PATH_TYPE_PARENT) { + apdu.p1 = 0x03; + pathlen = 0; + apdu.cse = SC_APDU_CASE_2_SHORT; + } + else if (lpath.type == SC_PATH_TYPE_DF_NAME) { + apdu.p1 = 0x04; + } + else { + sc_log(ctx, "Invalid PATH type: 0x%X", lpath.type); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "iasecc_select_file() invalid PATH type"); + } + + for (ii=0; ii<2; ii++) { + apdu.lc = pathlen; + apdu.data = lpath.value; + apdu.datalen = pathlen; + + if (apdu.cse == SC_APDU_CASE_4_SHORT || apdu.cse == SC_APDU_CASE_2_SHORT) { + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 256; + } + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (rv == SC_ERROR_INCORRECT_PARAMETERS && lpath.type == SC_PATH_TYPE_DF_NAME && apdu.p2 == 0x00) { + apdu.p2 = 0x0C; + continue; + } + + if (ii) { + /* 'SELECT AID' do not returned FCP. + * Use dummy FCP with NONE 'create file' ACL. + */ + unsigned char dummy_df_fcp[] = { + 0x62,0xFF, + 0x82,0x01,0x38, + 0x8A,0x01,0x05, + 0xA1,0x04,0x8C,0x02,0x02,0x00, + 0x84,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + }; + + memcpy(dummy_df_fcp + 16, apdu.data, apdu.datalen); + dummy_df_fcp[15] = apdu.datalen; + dummy_df_fcp[1] = apdu.datalen + 14; + + memcpy(apdu.resp, dummy_df_fcp, apdu.datalen + 16); + apdu.resplen = apdu.datalen + 16; + } + + break; + } + LOG_TEST_RET(ctx, rv, "iasecc_select_file() check SW failed"); + + sc_log(ctx, "iasecc_select_file() apdu.resp %i", apdu.resplen); + if (apdu.resplen) { + sc_log(ctx, "apdu.resp %02X:%02X:%02X...", apdu.resp[0], apdu.resp[1], apdu.resp[2]); + + switch (apdu.resp[0]) { + case 0x62: + case 0x6F: + file = sc_file_new(); + if (file == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + file->path = lpath; + + rv = iasecc_process_fci(card, file, apdu.resp, apdu.resplen); + if (rv) + LOG_FUNC_RETURN(ctx, rv); + break; + default: + LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } + + sc_log(ctx, "FileType %i", file->type); + if (file->type == SC_FILE_TYPE_DF) { + if (card->cache.valid && card->cache.current_df) + sc_file_free(card->cache.current_df); + card->cache.current_df = NULL; + + + if (card->cache.valid && card->cache.current_ef) + sc_file_free(card->cache.current_ef); + card->cache.current_ef = NULL; + + sc_file_dup(&card->cache.current_df, file); + card->cache.valid = 1; + } + else { + if (card->cache.valid && card->cache.current_ef) + sc_file_free(card->cache.current_ef); + + card->cache.current_ef = NULL; + + sc_file_dup(&card->cache.current_ef, file); + } + + if (file_out) + *file_out = file; + else + sc_file_free(file); + } + else if (lpath.type == SC_PATH_TYPE_DF_NAME) { + if (card->cache.current_df) + sc_file_free(card->cache.current_df); + card->cache.current_df = NULL; + + if (card->cache.current_ef) + sc_file_free(card->cache.current_ef); + card->cache.current_ef = NULL; + + card->cache.valid = 1; + } + } + + sc_print_cache(card); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_process_fci(struct sc_card *card, struct sc_file *file, + const unsigned char *buf, size_t buflen) +{ + struct sc_context *ctx = card->ctx; + size_t taglen; + int rv, ii, offs; + const unsigned char *acls = NULL, *tag = NULL; + unsigned char mask; + unsigned char ops_DF[7] = { + SC_AC_OP_DELETE, 0xFF, SC_AC_OP_ACTIVATE, SC_AC_OP_DEACTIVATE, 0xFF, SC_AC_OP_CREATE, 0xFF + }; + unsigned char ops_EF[7] = { + SC_AC_OP_DELETE, 0xFF, SC_AC_OP_ACTIVATE, SC_AC_OP_DEACTIVATE, 0xFF, SC_AC_OP_UPDATE, SC_AC_OP_READ + }; + + LOG_FUNC_CALLED(ctx); + + tag = sc_asn1_find_tag(ctx, buf, buflen, 0x6F, &taglen); + sc_log(ctx, "processing FCI: 0x6F tag %p", tag); + if (tag != NULL) { + sc_log(ctx, " FCP length %i", taglen); + buf = tag; + buflen = taglen; + } + + tag = sc_asn1_find_tag(ctx, buf, buflen, 0x62, &taglen); + sc_log(ctx, "processing FCI: 0x62 tag %p", tag); + if (tag != NULL) { + sc_log(ctx, " FCP length %i", taglen); + buf = tag; + buflen = taglen; + } + + rv = iso_ops->process_fci(card, file, buf, buflen); + LOG_TEST_RET(ctx, rv, "ISO parse FCI failed"); + + // Gemalto: 6F 19 80 02 02 ED 82 01 01 83 02 B0 01 88 00 8C 07 7B 17 17 17 17 17 00 8A 01 05 90 00 + // Sagem: 6F 17 62 15 80 02 00 7D 82 01 01 8C 02 01 00 83 02 2F 00 88 01 F0 8A 01 05 90 00 + // Oberthur: 62 1B 80 02 05 DC 82 01 01 83 02 B0 01 88 00 A1 09 8C 07 7B 17 FF 17 17 17 00 8A 01 05 90 00 + + sc_log(ctx, "iasecc_process_fci() type %i; let's parse file ACLs", file->type); + tag = sc_asn1_find_tag(ctx, buf, buflen, IASECC_DOCP_TAG_ACLS, &taglen); + if (tag) + acls = sc_asn1_find_tag(ctx, tag, taglen, IASECC_DOCP_TAG_ACLS_CONTACT, &taglen); + else + acls = sc_asn1_find_tag(ctx, buf, buflen, IASECC_DOCP_TAG_ACLS_CONTACT, &taglen); + + if (!acls) { + sc_log(ctx, "ACLs not found in data(%i) %s", buflen, sc_dump_hex(buf, buflen)); + LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "ACLs tag missing"); + } + + sc_log(ctx, "ACLs(%i) '%s'", taglen, sc_dump_hex(acls, taglen)); + mask = 0x40, offs = 1; + for (ii = 0; ii < 7; ii++, mask /= 2) { + unsigned char op = file->type == SC_FILE_TYPE_DF ? ops_DF[ii] : ops_EF[ii]; + + if (!(mask & acls[0])) + continue; + + sc_log(ctx, "ACLs mask 0x%X, offs %i, op 0x%X, acls[offs] 0x%X", mask, offs, op, acls[offs]); + if (op == 0xFF) { + ; + } + else if (acls[offs] == 0) { + sc_file_add_acl_entry(file, op, SC_AC_NONE, 0); + } + else if (acls[offs] == 0xFF) { + sc_file_add_acl_entry(file, op, SC_AC_NEVER, 0); + } + else if ((acls[offs] & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_USER_AUTH) { + sc_file_add_acl_entry(file, op, SC_AC_SEN, acls[offs] & IASECC_SCB_METHOD_MASK_REF); + } + else if (acls[offs] & IASECC_SCB_METHOD_MASK) { + sc_file_add_acl_entry(file, op, SC_AC_SCB, acls[offs]); + } + else { + sc_log(ctx, "Warning: non supported SCB method: %X", acls[offs]); + sc_file_add_acl_entry(file, op, SC_AC_NEVER, 0); + } + + offs++; + } + + LOG_FUNC_RETURN(ctx, 0); +} + + +static int +iasecc_fcp_encode(struct sc_card *card, struct sc_file *file, unsigned char *out, size_t out_len) +{ + struct sc_context *ctx = card->ctx; + unsigned char buf[0x80], type; + unsigned char ops[7] = { + SC_AC_OP_DELETE, 0xFF, SC_AC_OP_ACTIVATE, SC_AC_OP_DEACTIVATE, 0xFF, SC_AC_OP_UPDATE, SC_AC_OP_READ + }; + unsigned char smbs[8]; + size_t ii, offs = 0, amb, mask, nn_smb; + + LOG_FUNC_CALLED(ctx); + + if (file->type == SC_FILE_TYPE_DF) + type = IASECC_FCP_TYPE_DF; + else + type = IASECC_FCP_TYPE_EF; + + buf[offs++] = IASECC_FCP_TAG_SIZE; + buf[offs++] = 2; + buf[offs++] = (file->size >> 8) & 0xFF; + buf[offs++] = file->size & 0xFF; + + buf[offs++] = IASECC_FCP_TAG_TYPE; + buf[offs++] = 1; + buf[offs++] = type; + + buf[offs++] = IASECC_FCP_TAG_FID; + buf[offs++] = 2; + buf[offs++] = (file->id >> 8) & 0xFF; + buf[offs++] = file->id & 0xFF; + + buf[offs++] = IASECC_FCP_TAG_SFID; + buf[offs++] = 0; + + amb = 0, mask = 0x40, nn_smb = 0; + for (ii = 0; ii < sizeof(ops); ii++, mask >>= 1) { + const struct sc_acl_entry *entry; + + if (ops[ii]==0xFF) + continue; + + entry = sc_file_get_acl_entry(file, ops[ii]); + sc_log(ctx, "method %X; reference %X", entry->method, entry->key_ref); + + if (entry->method == SC_AC_NEVER) + continue; + else if (entry->method == SC_AC_NONE) + smbs[nn_smb++] = 0x00; + else if (entry->method == SC_AC_CHV) + smbs[nn_smb++] = entry->key_ref | IASECC_SCB_METHOD_USER_AUTH; + else if (entry->method == SC_AC_SEN) + smbs[nn_smb++] = entry->key_ref | IASECC_SCB_METHOD_USER_AUTH; + else if (entry->method == SC_AC_SCB) + smbs[nn_smb++] = entry->key_ref; + else if (entry->method == SC_AC_PRO) + smbs[nn_smb++] = entry->key_ref | IASECC_SCB_METHOD_SM; + else + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Non supported AC method"); + + amb |= mask; + sc_log(ctx, "%i: AMB %X; nn_smb %i", ii, amb, nn_smb); + } + + printf("TODO: Encode contactless ACLs and life cycle status for all IAS/ECC cards\n"); + if (card->type == SC_CARD_TYPE_IASECC_SAGEM) { + unsigned char status = 0; + + buf[offs++] = IASECC_FCP_TAG_ACLS; + buf[offs++] = 2*(2 + 1 + nn_smb); + + buf[offs++] = IASECC_FCP_TAG_ACLS_CONTACT; + buf[offs++] = nn_smb + 1; + buf[offs++] = amb; + memcpy(buf + offs, smbs, nn_smb); + offs += nn_smb; + + /* Same ACLs for contactless */ + buf[offs++] = IASECC_DOCP_TAG_ACLS_CONTACTLESS; + buf[offs++] = nn_smb + 1; + buf[offs++] = amb; + memcpy(buf + offs, smbs, nn_smb); + offs += nn_smb; + + if (file->status == SC_FILE_STATUS_ACTIVATED) + status = 0x05; + else if (file->status == SC_FILE_STATUS_CREATION) + status = 0x01; + + if (status) { + buf[offs++] = 0x8A; + buf[offs++] = 0x01; + buf[offs++] = status; + } + } + else { + buf[offs++] = IASECC_FCP_TAG_ACLS; + buf[offs++] = 2 + 1 + nn_smb; + + buf[offs++] = IASECC_FCP_TAG_ACLS_CONTACT; + buf[offs++] = nn_smb + 1; + buf[offs++] = amb; + memcpy(buf + offs, smbs, nn_smb); + offs += nn_smb; + } + + if (out) { + if (out_len < offs) + LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Buffer too small to encode FCP"); + memcpy(out, buf, offs); + } + + LOG_FUNC_RETURN(ctx, offs); +} + + +static int +iasecc_create_file(struct sc_card *card, struct sc_file *file) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + const struct sc_acl_entry *entry = NULL; + unsigned char sbuf[0x100]; + size_t sbuf_len; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_print_cache(card); + + if (file->type != SC_FILE_TYPE_WORKING_EF) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Creation of the file with of this type is not supported"); + + sbuf_len = iasecc_fcp_encode(card, file, sbuf + 2, sizeof(sbuf)-2); + LOG_TEST_RET(ctx, sbuf_len, "FCP encode error"); + + sbuf[0] = IASECC_FCP_TAG; + sbuf[1] = sbuf_len; + + if (card->cache.valid && card->cache.current_df) { + entry = sc_file_get_acl_entry(card->cache.current_df, SC_AC_OP_CREATE); + sc_log(ctx, "iasecc_create_file() 'CREATE' method/reference %X/%X", entry->method, entry->key_ref); + sc_log(ctx, "iasecc_create_file() create data: '%s'", sc_dump_hex(sbuf, sbuf_len + 2)); + if (entry->method == SC_AC_SCB && (entry->key_ref & IASECC_SCB_METHOD_SM)) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0, 0); + apdu.data = sbuf; + apdu.datalen = sbuf_len + 2; + apdu.lc = sbuf_len + 2; + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "iasecc_create_file() create file error"); + + rv = iasecc_select_file(card, &file->path, NULL); + LOG_TEST_RET(ctx, rv, "Cannot select newly created file"); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_logout(struct sc_card *card) +{ + struct sc_context *ctx = card->ctx; + struct sc_path path; + int rv; + + LOG_FUNC_CALLED(ctx); + if (!card->ef_atr || !card->ef_atr->aid.len) + return SC_SUCCESS; + + memset(&path, 0, sizeof(struct sc_path)); + path.type = SC_PATH_TYPE_DF_NAME; + memcpy(path.value, card->ef_atr->aid.value, card->ef_atr->aid.len); + path.len = card->ef_atr->aid.len; + + rv = iasecc_select_file(card, &path, NULL); + sc_log(ctx, "Select ECC ROOT with the AID from EF.ATR: rv %i", rv); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_finish(struct sc_card *card) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_private_data *private_data = (struct iasecc_private_data *)card->drv_data; + + LOG_FUNC_CALLED(ctx); + + if (private_data->se_info) { + if (private_data->se_info->df) + sc_file_free(private_data->se_info->df); + free(private_data->se_info); + } + + free(card->drv_data); + card->drv_data = NULL; + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_delete_file(struct sc_card *card, const struct sc_path *path) +{ + struct sc_context *ctx = card->ctx; + const struct sc_acl_entry *entry = NULL; + struct sc_apdu apdu; + struct sc_file *file = NULL; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_print_cache(card); + + rv = iasecc_select_file(card, path, &file); + if (rv == SC_ERROR_FILE_NOT_FOUND) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + LOG_TEST_RET(ctx, rv, "Cannot select file to delete"); + + entry = sc_file_get_acl_entry(file, SC_AC_OP_DELETE); + sc_log(ctx, "DELETE method/reference %X/%X", entry->method, entry->key_ref); + + if (entry->method == SC_AC_SCB && (entry->key_ref & IASECC_SCB_METHOD_SM)) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xE4, 0x00, 0x00); + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "Delete file failed"); + + if (card->cache.valid && card->cache.current_ef) + sc_file_free(card->cache.current_ef); + card->cache.current_ef = NULL; + + LOG_FUNC_RETURN(ctx, rv); + + + LOG_FUNC_CALLED(ctx); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + +} + + +static int +iasecc_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2) +{ + if (sw1 == 0x62 && sw2 == 0x82) + return SC_SUCCESS; + + return iso_ops->check_sw(card, sw1, sw2); +} + + +#if 0 +const struct sc_supported_algo_info * +iasecc_get_algorithm(struct sc_context *ctx, const struct sc_security_env *env, + unsigned operation, unsigned mechanism) +{ + const struct sc_supported_algo_info *info = NULL; + int ii; + + if (!env) + return NULL; + + for (ii=0;iisupported_algos[ii].reference; ii++) + if ((env->supported_algos[ii].operations & operation) + && (env->supported_algos[ii].mechanism == mechanism)) + break; + + if (ii < SC_MAX_SUPPORTED_ALGORITHMS && env->supported_algos[ii].reference) { + info = &env->supported_algos[ii]; + sc_log(ctx, "found IAS/ECC algorithm %X:%X:%X:%X", + info->reference, info->mechanism, info->operations, info->algo_ref); + } + else { + sc_log(ctx, "cannot find IAS/ECC algorithm (operation:%X,mechanism:%X)", operation, mechanism); + } + + return info; +} +#else +static unsigned +iasecc_get_algorithm(struct sc_context *ctx, const struct sc_security_env *env, + unsigned operation, unsigned mechanism) +{ + const struct sc_supported_algo_info *info = NULL; + int ii; + + if (!env) + return 0; + + for (ii=0;iisupported_algos[ii].reference; ii++) + if ((env->supported_algos[ii].operations & operation) + && (env->supported_algos[ii].mechanism == mechanism)) + break; + + if (ii < SC_MAX_SUPPORTED_ALGORITHMS && env->supported_algos[ii].reference) { + info = &env->supported_algos[ii]; + sc_log(ctx, "found IAS/ECC algorithm %X:%X:%X:%X", + info->reference, info->mechanism, info->operations, info->algo_ref); + } + else { + sc_log(ctx, "cannot find IAS/ECC algorithm (operation:%X,mechanism:%X)", operation, mechanism); + } + + return info ? info->algo_ref : 0; +} +#endif + + +static int +iasecc_se_cache_info(struct sc_card *card, struct iasecc_se_info *se) +{ + struct iasecc_private_data *prv = (struct iasecc_private_data *) card->drv_data; + struct sc_context *ctx = card->ctx; + struct iasecc_se_info *se_info = NULL, *si = NULL; + int rv; + + LOG_FUNC_CALLED(ctx); + + se_info = calloc(1, sizeof(struct iasecc_se_info)); + if (!se_info) + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "SE info allocation error"); + memcpy(se_info, se, sizeof(struct iasecc_se_info)); + + if (card->cache.valid && card->cache.current_df) { + sc_file_dup(&se_info->df, card->cache.current_df); + if (se_info->df == NULL) + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current DF file"); + } + + rv = iasecc_docp_copy(ctx, &se->docp, &se_info->docp); + LOG_TEST_RET(ctx, rv, "Cannot make copy of DOCP"); + + if (!prv->se_info) { + prv->se_info = se_info; + } + else { + for (si = prv->se_info; si->next; si = si->next) + ; + si->next = se_info; + } + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_se_get_info_from_cache(struct sc_card *card, struct iasecc_se_info *se) +{ + struct iasecc_private_data *prv = (struct iasecc_private_data *) card->drv_data; + struct sc_context *ctx = card->ctx; + struct iasecc_se_info *si = NULL; + int rv; + + LOG_FUNC_CALLED(ctx); + + for(si = prv->se_info; si; si = si->next) { + if (si->reference != se->reference) + continue; + if (!(card->cache.valid && card->cache.current_df) && si->df) + continue; + if (card->cache.valid && card->cache.current_df && !si->df) + continue; + if (card->cache.valid && card->cache.current_df && si->df) + if (memcmp(&card->cache.current_df->path, &si->df->path, sizeof(struct sc_path))) + continue; + break; + } + + if (!si) + return SC_ERROR_OBJECT_NOT_FOUND; + + memcpy(se, si, sizeof(struct iasecc_se_info)); + + if (si->df) { + sc_file_dup(&se->df, si->df); + if (se->df == NULL) + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current DF file"); + } + + rv = iasecc_docp_copy(ctx, &si->docp, &se->docp); + LOG_TEST_RET(ctx, rv, "Cannot make copy of DOCP"); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_se_get_info(struct sc_card *card, struct iasecc_se_info *se) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + unsigned char rbuf[0x100]; + unsigned char sbuf_iasecc[10] = { + 0x4D, 0x08, IASECC_SDO_TEMPLATE_TAG, 0x06, + IASECC_SDO_TAG_HEADER, IASECC_SDO_CLASS_SE | IASECC_OBJECT_REF_LOCAL, + se->reference & 0x3F, + 0x02, IASECC_SDO_CLASS_SE, 0x80 + }; + int rv; + + LOG_FUNC_CALLED(ctx); + + if (se->reference > IASECC_SE_REF_MAX) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + rv = iasecc_se_get_info_from_cache(card, se); + if (rv == SC_ERROR_OBJECT_NOT_FOUND) { + sc_log(ctx, "No SE#%X info in cache, try to use 'GET DATA'", se->reference); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xCB, 0x3F, 0xFF); + apdu.data = sbuf_iasecc; + apdu.datalen = sizeof(sbuf_iasecc); + apdu.lc = apdu.datalen; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = sizeof(rbuf); + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "get SE data error"); + + rv = iasecc_se_parse(card, apdu.resp, apdu.resplen, se); + LOG_TEST_RET(ctx, rv, "cannot parse SE data"); + + rv = iasecc_se_cache_info(card, se); + LOG_TEST_RET(ctx, rv, "failed to put SE data into cache"); + } + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_set_security_env(struct sc_card *card, + const struct sc_security_env *env, int se_num) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_sdo sdo; + struct iasecc_private_data *prv = (struct iasecc_private_data *) card->drv_data; + //const struct sc_supported_algo_info *algo_info = NULL; + unsigned algo_ref; + struct sc_apdu apdu; + unsigned sign_meth, sign_ref, auth_meth, auth_ref, aflags; + unsigned char cse_crt_at[] = { + 0x84, 0x01, 0xFF, + 0x80, 0x01, IASECC_ALGORITHM_RSA_PKCS + }; + unsigned char cse_crt_dst[] = { + 0x84, 0x01, 0xFF, + 0x80, 0x01, (IASECC_ALGORITHM_RSA_PKCS | IASECC_ALGORITHM_SHA1) + }; + unsigned char cse_crt_ht[] = { + 0x80, 0x01, IASECC_ALGORITHM_SHA1 + }; + unsigned char cse_crt_ct[] = { + 0x84, 0x01, 0xFF, + 0x80, 0x01, (IASECC_ALGORITHM_RSA_PKCS_DECRYPT | IASECC_ALGORITHM_SHA1) + }; + int rv, operation = env->operation; + + printf("TODO: take algorithm references from 5032, not from header file.\n"); + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "iasecc_set_security_env(card:%p) operation 0x%X; senv.algorithm 0x%X, senv.algorithm_ref 0x%X", + card, env->operation, env->algorithm, env->algorithm_ref); + + memset(&sdo, 0, sizeof(sdo)); + sdo.sdo_class = IASECC_SDO_CLASS_RSA_PRIVATE; + sdo.sdo_ref = env->key_ref[0] & ~IASECC_OBJECT_REF_LOCAL; + rv = iasecc_sdo_get_data(card, &sdo); + LOG_TEST_RET(ctx, rv, "Cannot get RSA PRIVATE SDO data"); + + /* To made by iasecc_sdo_convert_to_file() */ + prv->key_size = *(sdo.docp.size.value + 0) * 0x100 + *(sdo.docp.size.value + 1); + sc_log(ctx, "prv->key_size 0x%X", prv->key_size); + + rv = iasecc_sdo_convert_acl(card, &sdo, SC_AC_OP_PSO_COMPUTE_SIGNATURE, &sign_meth, &sign_ref); + LOG_TEST_RET(ctx, rv, "Cannot convert SC_AC_OP_SIGN acl"); + + rv = iasecc_sdo_convert_acl(card, &sdo, SC_AC_OP_INTERNAL_AUTHENTICATE, &auth_meth, &auth_ref); + LOG_TEST_RET(ctx, rv, "Cannot convert SC_AC_OP_INT_AUTH acl"); + + aflags = env->algorithm_flags; + + if (!(aflags & SC_ALGORITHM_RSA_PAD_PKCS1)) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Only supported signature with PKCS1 padding"); + + if (operation == SC_SEC_OPERATION_SIGN) { + if (!(aflags & (SC_ALGORITHM_RSA_HASH_SHA1 | SC_ALGORITHM_RSA_HASH_SHA256))) { + sc_log(ctx, "CKM_RSA_PKCS asked -- use 'AUTHENTICATE' sign operation instead of 'SIGN'"); + operation = SC_SEC_OPERATION_AUTHENTICATE; + } + else if (sign_meth == SC_AC_NEVER) { + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PSO_DST not allowed for this key"); + } + } + + if (operation == SC_SEC_OPERATION_SIGN) { + prv->op_method = sign_meth; + prv->op_ref = sign_ref; + } + else if (operation == SC_SEC_OPERATION_AUTHENTICATE) { + if (auth_meth == SC_AC_NEVER) + LOG_TEST_RET(ctx, SC_ERROR_NOT_ALLOWED, "INTERNAL_AUTHENTICATE is not allowed for this key"); + + prv->op_method = auth_meth; + prv->op_ref = auth_ref; + } + + sc_log(ctx, "senv.algorithm 0x%X, senv.algorithm_ref 0x%X", env->algorithm, env->algorithm_ref); + sc_log(ctx, "se_num %i, operation 0x%X, algorithm 0x%X, algorithm_ref 0x%X, flags 0x%X; key size %i", + se_num, operation, env->algorithm, env->algorithm_ref, env->algorithm_flags, prv->key_size); + switch (operation) { + case SC_SEC_OPERATION_SIGN: + if (!(env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Need RSA_PKCS1 specified"); + + if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA256) { + algo_ref = iasecc_get_algorithm(ctx, env, SC_PKCS15_ALGO_OP_HASH, CKM_SHA256); + if (!algo_ref) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Card application do not supports HASH:SHA256"); + + cse_crt_ht[2] = algo_ref; /* IASECC_ALGORITHM_SHA2 */ + + algo_ref = iasecc_get_algorithm(ctx, env, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_SHA256_RSA_PKCS); + if (!algo_ref) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Card application do not supports SIGNATURE:SHA1_RSA_PKCS"); + + cse_crt_dst[2] = env->key_ref[0] | IASECC_OBJECT_REF_LOCAL; + cse_crt_dst[5] = algo_ref; /* IASECC_ALGORITHM_RSA_PKCS | IASECC_ALGORITHM_SHA2 */ + } + else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) { + algo_ref = iasecc_get_algorithm(ctx, env, SC_PKCS15_ALGO_OP_HASH, CKM_SHA_1); + if (!algo_ref) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Card application do not supports HASH:SHA1"); + + cse_crt_ht[2] = algo_ref; /* IASECC_ALGORITHM_SHA1 */ + + algo_ref = iasecc_get_algorithm(ctx, env, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_SHA1_RSA_PKCS); + if (!algo_ref) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Card application do not supports SIGNATURE:SHA1_RSA_PKCS"); + + cse_crt_dst[2] = env->key_ref[0] | IASECC_OBJECT_REF_LOCAL; + cse_crt_dst[5] = algo_ref; /* IASECC_ALGORITHM_RSA_PKCS | IASECC_ALGORITHM_SHA1 */ + } + else { + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Need RSA_HASH_SHA[1,256] specified"); + } + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, IASECC_CRT_TAG_HT); + apdu.data = cse_crt_ht; + apdu.datalen = sizeof(cse_crt_ht); + apdu.lc = sizeof(cse_crt_ht); + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "MSE restore error"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, IASECC_CRT_TAG_DST); + apdu.data = cse_crt_dst; + apdu.datalen = sizeof(cse_crt_dst); + apdu.lc = sizeof(cse_crt_dst); + break; + case SC_SEC_OPERATION_AUTHENTICATE: + algo_ref = iasecc_get_algorithm(ctx, env, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_RSA_PKCS); + if (!algo_ref) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Application do not supports SIGNATURE:RSA_PKCS"); + + cse_crt_at[2] = env->key_ref[0] | IASECC_OBJECT_REF_LOCAL; + cse_crt_at[5] = algo_ref; /* IASECC_ALGORITHM_RSA_PKCS */ + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, IASECC_CRT_TAG_AT); + apdu.data = cse_crt_at; + apdu.datalen = sizeof(cse_crt_at); + apdu.lc = sizeof(cse_crt_at); + break; + case SC_SEC_OPERATION_DECIPHER: + rv = iasecc_sdo_convert_acl(card, &sdo, SC_AC_OP_PSO_DECRYPT, &prv->op_method, &prv->op_ref); + LOG_TEST_RET(ctx, rv, "Cannot convert SC_AC_OP_PSO_DECRYPT acl"); + algo_ref = iasecc_get_algorithm(ctx, env, SC_PKCS15_ALGO_OP_DECIPHER, CKM_RSA_PKCS); + if (!algo_ref) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Application do not supports DECHIPHER:RSA_PKCS"); + + cse_crt_ct[2] = env->key_ref[0] | IASECC_OBJECT_REF_LOCAL; + cse_crt_ct[5] = algo_ref; /* IASECC_ALGORITHM_RSA_PKCS_DECRYPT | IASECC_ALGORITHM_SHA1 */ + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, IASECC_CRT_TAG_CT); + apdu.data = cse_crt_ct; + apdu.datalen = sizeof(cse_crt_ct); + apdu.lc = sizeof(cse_crt_ct); + break; + default: + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + } + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "MSE restore error"); + + prv->security_env = *env; + prv->security_env.operation = operation; + + LOG_FUNC_RETURN(ctx, 0); +} + + +static int +iasecc_chv_verify_pinpad(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, int *tries_left) +{ + struct sc_context *ctx = card->ctx; + unsigned char ffs[0x100]; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "CHV PINPAD PIN reference %i", pin_cmd->pin_reference); + + rv = iasecc_pin_is_verified(card, pin_cmd, tries_left); + if (!rv) + LOG_FUNC_RETURN(ctx, rv); + + if (!card->reader || !card->reader->ops || !card->reader->ops->perform_verify) { + sc_log(ctx, "Reader not ready for PIN PAD"); + LOG_FUNC_RETURN(ctx, SC_ERROR_READER); + } + + if (pin_cmd->pin1.min_length != pin_cmd->pin1.max_length) { + sc_log(ctx, "Different values for PIN min and max lengths is not actually compatible with PinPAD."); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, + "Different values for PIN min and max lengths is not actually compatible with PinPAD."); + } + + pin_cmd->pin1.len = pin_cmd->pin1.min_length; + + memset(ffs, 0xFF, sizeof(ffs)); + pin_cmd->pin1.data = ffs; + + pin_cmd->cmd = SC_PIN_CMD_VERIFY; + pin_cmd->flags |= SC_PIN_CMD_USE_PINPAD; + + /* + if (card->reader && card->reader->ops && card->reader->ops->load_message) { + rv = card->reader->ops->load_message(card->reader, card->slot, 0, "Here we are!"); + sc_log(ctx, "Load message returned %i", rv); + } + */ + + rv = iso_ops->pin_cmd(card, pin_cmd, tries_left); + sc_log(ctx, "rv %i", rv); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd, + int *tries_left) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "CHV PIN reference %i, data_len %i", pin_cmd->pin_reference, pin_cmd->pin1.len); + + if (pin_cmd->pin1.data && !pin_cmd->pin1.len) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0, pin_cmd->pin_reference); + } + else if (pin_cmd->pin1.data && pin_cmd->pin1.len) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, pin_cmd->pin_reference); + apdu.data = pin_cmd->pin1.data; + apdu.datalen = pin_cmd->pin1.len; + apdu.lc = pin_cmd->pin1.len; + } + else if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD) && !pin_cmd->pin1.data && !pin_cmd->pin1.len) { + rv = iasecc_chv_verify_pinpad(card, pin_cmd, tries_left); + sc_log(ctx, "Result of verifying CHV with PIN pad %i", rv); + LOG_FUNC_RETURN(ctx, rv); + } + else { + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + } + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(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); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_se_at_to_chv_reference(struct sc_card *card, unsigned reference, + unsigned *chv_reference) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_se_info se; + struct sc_crt crt; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "SE reference %i", reference); + + if (reference > IASECC_SE_REF_MAX) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + memset(&se, 0, sizeof(se)); + se.reference = reference; + + rv = iasecc_se_get_info(card, &se); + LOG_TEST_RET(ctx, rv, "SDO get data error"); + + memset(&crt, 0, sizeof(crt)); + crt.tag = IASECC_CRT_TAG_AT; + crt.usage = IASECC_UQB_AT_USER_PASSWORD; + + rv = iasecc_se_get_crt(card, &se, &crt); + LOG_TEST_RET(ctx, rv, "no authentication template for USER PASSWORD"); + + if (chv_reference) + *chv_reference = crt.refs[0]; + + if (se.df) + sc_file_free(se.df); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_pin_is_verified(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd_data, + int *tries_left) +{ + struct sc_context *ctx = card->ctx; + struct sc_pin_cmd_data pin_cmd; + struct sc_acl_entry acl = pin_cmd_data->pin1.acls[IASECC_ACLS_CHV_VERIFY]; + int rv = SC_ERROR_SECURITY_STATUS_NOT_SATISFIED; + + LOG_FUNC_CALLED(ctx); + + if (pin_cmd_data->pin_type != SC_AC_CHV) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "PIN type is not supported for the verification"); + + sc_log(ctx, "Verify ACL(method:%X;ref:%X)", acl.method, acl.key_ref); + if (acl.method != IASECC_SCB_ALWAYS) + LOG_FUNC_RETURN(ctx, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED); + + pin_cmd = *pin_cmd_data; + pin_cmd.pin1.data = (unsigned char *)""; + pin_cmd.pin1.len = 0; + + rv = iasecc_chv_verify(card, &pin_cmd, tries_left); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_pin_verify(struct sc_card *card, unsigned type, unsigned reference, + const unsigned char *data, size_t data_len, int *tries_left) +{ + struct sc_context *ctx = card->ctx; + struct sc_pin_cmd_data pin_cmd; + unsigned chv_ref = reference; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "Verify PIN(type:%X,ref:%i,data(len:%i,%p)", type, reference, data_len, data); + + if (type == SC_AC_SCB) { + if (reference & IASECC_SCB_METHOD_USER_AUTH) { + type = SC_AC_SEN; + reference = reference & IASECC_SCB_METHOD_MASK_REF; + } + else { + sc_log(ctx, "Do not try to verify non CHV PINs"); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + } + } + + if (type == SC_AC_SEN) { + rv = iasecc_se_at_to_chv_reference(card, reference, &chv_ref); + LOG_TEST_RET(ctx, rv, "SE AT to CHV reference error"); + } + + memset(&pin_cmd, 0, sizeof(pin_cmd)); + pin_cmd.pin_type = SC_AC_CHV; + pin_cmd.pin_reference = chv_ref; + pin_cmd.cmd = SC_PIN_CMD_VERIFY; + + rv = iasecc_pin_get_policy(card, &pin_cmd); + LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); + + pin_cmd.pin1.data = data; + pin_cmd.pin1.len = data_len; + + rv = iasecc_pin_is_verified(card, &pin_cmd, tries_left); + if (data && !data_len) + LOG_FUNC_RETURN(ctx, rv); + + if (!rv) { + if (iasecc_chv_cache_is_verified(card, &pin_cmd)) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + } + else if (rv != SC_ERROR_PIN_CODE_INCORRECT && rv != SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { + LOG_FUNC_RETURN(ctx, rv); + } + + iasecc_chv_cache_clean(card, &pin_cmd); + + rv = iasecc_chv_verify(card, &pin_cmd, tries_left); + LOG_TEST_RET(ctx, rv, "PIN CHV verification error"); + + rv = iasecc_chv_cache_verified(card, &pin_cmd); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_chv_change_pinpad(struct sc_card *card, unsigned reference, int *tries_left) +{ + struct sc_context *ctx = card->ctx; + struct sc_pin_cmd_data pin_cmd; + unsigned char pin1_data[0x100], pin2_data[0x100]; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "CHV PINPAD PIN reference %i", reference); + + memset(pin1_data, 0xFF, sizeof(pin1_data)); + memset(pin2_data, 0xFF, sizeof(pin2_data)); + + if (!card->reader || !card->reader->ops || !card->reader->ops->perform_verify) { + sc_log(ctx, "Reader not ready for PIN PAD"); + LOG_FUNC_RETURN(ctx, SC_ERROR_READER); + } + + memset(&pin_cmd, 0, sizeof(pin_cmd)); + pin_cmd.pin_type = SC_AC_CHV; + pin_cmd.pin_reference = reference; + pin_cmd.cmd = SC_PIN_CMD_CHANGE; + pin_cmd.flags |= SC_PIN_CMD_USE_PINPAD; + + rv = iasecc_pin_get_policy(card, &pin_cmd); + LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); + + if (pin_cmd.pin1.min_length != pin_cmd.pin1.max_length) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Different values for PIN min and max lengths is not allowed with PinPAD."); + + if (pin_cmd.pin1.min_length < 4) + pin_cmd.pin1.min_length = 4; + pin_cmd.pin1.len = pin_cmd.pin1.min_length; + pin_cmd.pin1.data = pin1_data; + + memcpy(&pin_cmd.pin2, &pin_cmd.pin1, sizeof(pin_cmd.pin1)); + pin_cmd.pin2.data = pin2_data; + + sc_log(ctx, "PIN1 max/min: %i/%i", pin_cmd.pin1.max_length, pin_cmd.pin1.min_length); + sc_log(ctx, "PIN2 max/min: %i/%i", pin_cmd.pin2.max_length, pin_cmd.pin2.min_length); + rv = iso_ops->pin_cmd(card, &pin_cmd, tries_left); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_chv_set_pinpad(struct sc_card *card, unsigned char reference) +{ + struct sc_context *ctx = card->ctx; + struct sc_pin_cmd_data pin_cmd; + unsigned char pin_data[0x100]; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "Set CHV PINPAD PIN reference %i", reference); + + memset(pin_data, 0xFF, sizeof(pin_data)); + + if (!card->reader || !card->reader->ops || !card->reader->ops->perform_verify) { + sc_log(ctx, "Reader not ready for PIN PAD"); + LOG_FUNC_RETURN(ctx, SC_ERROR_READER); + } + + memset(&pin_cmd, 0, sizeof(pin_cmd)); + pin_cmd.pin_type = SC_AC_CHV; + pin_cmd.pin_reference = reference; + pin_cmd.cmd = SC_PIN_CMD_UNBLOCK; + pin_cmd.flags |= SC_PIN_CMD_USE_PINPAD; + + rv = iasecc_pin_get_policy(card, &pin_cmd); + LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error"); + + if (pin_cmd.pin1.min_length != pin_cmd.pin1.max_length) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Different values for PIN min and max lengths is not allowed with PinPAD."); + + if (pin_cmd.pin1.min_length < 4) + pin_cmd.pin1.min_length = 4; + pin_cmd.pin1.len = pin_cmd.pin1.min_length; + pin_cmd.pin1.data = pin_data; + + memcpy(&pin_cmd.pin2, &pin_cmd.pin1, sizeof(pin_cmd.pin1)); + memset(&pin_cmd.pin1, 0, sizeof(pin_cmd.pin1)); + + sc_log(ctx, "PIN1(max:%i,min:%i)", pin_cmd.pin1.max_length, pin_cmd.pin1.min_length); + sc_log(ctx, "PIN2(max:%i,min:%i)", pin_cmd.pin2.max_length, pin_cmd.pin2.min_length); + + rv = iso_ops->pin_cmd(card, &pin_cmd, NULL); + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_pin_get_policy (struct sc_card *card, struct sc_pin_cmd_data *data) +{ + struct sc_context *ctx = card->ctx; + struct sc_file *save_current_df = NULL, *save_current_ef = NULL; + struct iasecc_sdo sdo; + struct sc_path path; + int ii, rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "iasecc_pin_get_policy(card:%p)", card); + + if (data->pin_type != SC_AC_CHV) { + sc_log(ctx, "To unblock PIN it's CHV reference should be presented"); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + if (card->cache.valid && card->cache.current_df) { + sc_file_dup(&save_current_df, card->cache.current_df); + if (save_current_df == NULL) + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current DF file"); + } + + if (card->cache.valid && card->cache.current_ef) { + sc_file_dup(&save_current_ef, card->cache.current_ef); + if (save_current_ef == NULL) + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current EF file"); + } + + if (!(data->pin_reference & IASECC_OBJECT_REF_LOCAL) && card->cache.valid && card->cache.current_df) { + sc_format_path("3F00", &path); + path.type = SC_PATH_TYPE_FILE_ID; + rv = iasecc_select_file(card, &path, NULL); + LOG_TEST_RET(ctx, rv, "Unable to select MF"); + } + + memset(&sdo, 0, sizeof(sdo)); + sdo.sdo_class = IASECC_SDO_CLASS_CHV; + + sdo.sdo_ref = data->pin_reference & ~IASECC_OBJECT_REF_LOCAL; + + sc_log(ctx, "iasecc_pin_get_policy() reference %i", sdo.sdo_ref); + + rv = iasecc_sdo_get_data(card, &sdo); + LOG_TEST_RET(ctx, rv, "Cannot get SDO PIN data"); + + if (sdo.docp.acls_contact.size == 0) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Extremely strange ... there is no ACLs"); + + for (ii=0; iipin1.acls[ii]; + int crt_num = 0; + + memset(&se, 0, sizeof(se)); + memset(&acl->crts, 0, sizeof(acl->crts)); + + sc_log(ctx, "iasecc_pin_get_policy() set info acls: SCB 0x%X", scb); + /* acl->raw_value = scb; */ + acl->method = scb & IASECC_SCB_METHOD_MASK; + acl->key_ref = scb & IASECC_SCB_METHOD_MASK_REF; + + + if (scb==0 || scb==0xFF) + continue; + + if (se.reference != acl->key_ref) { + memset(&se, 0, sizeof(se)); + + se.reference = acl->key_ref; + + rv = iasecc_se_get_info(card, &se); + LOG_TEST_RET(ctx, rv, "SDO get data error"); + } + + if (scb & IASECC_SCB_METHOD_USER_AUTH) { + rv = iasecc_se_get_crt_by_usage(card, &se, + IASECC_CRT_TAG_AT, IASECC_UQB_AT_USER_PASSWORD, &acl->crts[crt_num]); + LOG_TEST_RET(ctx, rv, "no authentication template for 'USER PASSWORD'"); + sc_log(ctx, "iasecc_pin_get_policy() scb:0x%X; sdo_ref:[%i,%i,...]", + scb, acl->crts[crt_num].refs[0], acl->crts[crt_num].refs[1]); + crt_num++; + } + + if (scb & IASECC_SCB_METHOD_SM) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, ""); + + if (scb & IASECC_SCB_METHOD_EXT_AUTH) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, ""); + + if (se.df) + sc_file_free(se.df); + } + + if (sdo.data.chv.size_max.value) + data->pin1.max_length = *sdo.data.chv.size_max.value; + if (sdo.data.chv.size_min.value) + data->pin1.min_length = *sdo.data.chv.size_min.value; + if (sdo.docp.tries_maximum.value) + data->pin1.max_tries = *sdo.docp.tries_maximum.value; + if (sdo.docp.tries_remaining.value) + data->pin1.tries_left = *sdo.docp.tries_remaining.value; + + data->pin1.encoding = SC_PIN_ENCODING_ASCII; + data->pin1.offset = 5; + + sc_log(ctx, "PIN policy: size max/min %i/%i, tries max/left %i/%i", + data->pin1.max_length, data->pin1.min_length, + data->pin1.max_tries, data->pin1.tries_left); + iasecc_sdo_free_fields(card, &sdo); + + if (save_current_df) { + struct sc_file *dummy_file = NULL; + + sc_log(ctx, "iasecc_pin_get_policy() restore current DF"); + rv = iasecc_select_file(card, &save_current_df->path, &dummy_file); + LOG_TEST_RET(ctx, rv, "Cannot return to saved DF"); + + sc_file_free(dummy_file); + sc_file_free(save_current_df); + } + + if (save_current_ef) { + struct sc_file *dummy_file = NULL; + + sc_log(ctx, "iasecc_pin_get_policy() restore current EF"); + rv = iasecc_select_file(card, &save_current_ef->path, &dummy_file); + LOG_TEST_RET(ctx, rv, "Cannot return to saved EF"); + + sc_file_free(dummy_file); + sc_file_free(save_current_ef); + } + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_pin_reset(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +{ + struct sc_context *ctx = card->ctx; + struct sc_file *save_current = NULL; + struct iasecc_sdo sdo; + struct sc_apdu apdu; + unsigned reference, scb; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "Reset PIN(ref:%i,lengths:%i/%i)", data->pin_reference, data->pin1.len, data->pin2.len); + + reference = data->pin_reference; + + if (!(data->pin_reference & IASECC_OBJECT_REF_LOCAL) + && card->cache.valid && card->cache.current_df) { + struct sc_path path; + + sc_file_dup(&save_current, card->cache.current_df); + if (save_current == NULL) + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "Cannot duplicate current DF file"); + + sc_format_path("3F00", &path); + path.type = SC_PATH_TYPE_FILE_ID; + rv = iasecc_select_file(card, &path, NULL); + LOG_TEST_RET(ctx, rv, "Unable to select MF"); + } + + memset(&sdo, 0, sizeof(sdo)); + sdo.sdo_class = IASECC_SDO_CLASS_CHV; + sdo.sdo_ref = reference & ~IASECC_OBJECT_REF_LOCAL; + + rv = iasecc_sdo_get_data(card, &sdo); + LOG_TEST_RET(ctx, rv, "Cannot get PIN data"); + + if (sdo.docp.acls_contact.size == 0) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Extremely strange ... there is no ACLs"); + + scb = sdo.docp.scbs[IASECC_ACLS_CHV_RESET]; + do { + unsigned need_all = scb & IASECC_SCB_METHOD_NEED_ALL ? 1 : 0; + int ignore_ext_auth = 0; + +#ifdef ALLOW_IGNORE_EXTERNAL_AUTHENTICATION + ignore_ext_auth = ((scb & IASECC_SCB_METHOD_EXT_AUTH) && !need_all && (scb & IASECC_SCB_METHOD_SM)); +#endif + if (scb & IASECC_SCB_METHOD_USER_AUTH) { + int puk_tries_left; + + sc_log(ctx, "Try to verify PUK code: pin1.data:%p, pin1.len:%i", data->pin1.data, data->pin1.len); + rv = iasecc_pin_verify(card, SC_AC_SEN, scb & IASECC_SCB_METHOD_MASK_REF, + data->pin1.data, data->pin1.len, &puk_tries_left); + sc_log(ctx, "Verify PUK code returned %i", rv); + LOG_TEST_RET(ctx, rv, "iasecc_pin_reset() PIN verification error"); + + if (!need_all) + break; + } + + if ((scb & IASECC_SCB_METHOD_EXT_AUTH) && !ignore_ext_auth) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); + + if (scb & IASECC_SCB_METHOD_SM) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); + } while(0); + + iasecc_sdo_free_fields(card, &sdo); + + if (data->pin2.len) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2C, 0x02, reference); + apdu.data = data->pin2.data; + apdu.datalen = data->pin2.len; + apdu.lc = apdu.datalen; + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "PIN cmd failed"); + } + else if (data->pin2.data) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x2C, 3, reference); + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "PIN cmd failed"); + } + else { + rv = iasecc_chv_set_pinpad(card, reference); + sc_log(ctx, "Set CHV with PIN pad returned %i", rv); + } + + if (save_current) { + struct sc_file *dummy_file = NULL; + + rv = iasecc_select_file(card, &save_current->path, &dummy_file); + LOG_TEST_RET(ctx, rv, "Cannot return to saved PATH"); + } + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + unsigned reference; + unsigned char pin_data[0x100]; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "iasecc_pin_cmd(card:%p) cmd 0x%X, PIN type 0x%X, PIN reference %i, PIN-1 %p:%i, PIN-2 %p:%i", + card, data->cmd, data->pin_type, data->pin_reference, + data->pin1.data, data->pin1.len, data->pin2.data, data->pin2.len); + + reference = data->pin_reference; + + switch (data->cmd) { + case SC_PIN_CMD_VERIFY: + rv = iasecc_pin_verify(card, data->pin_type, reference, data->pin1.data, data->pin1.len, tries_left); + LOG_TEST_RET(ctx, rv, "PIN verification error"); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + case SC_PIN_CMD_CHANGE: + if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD)) { + if (!data->pin1.data && !data->pin1.len && &data->pin2.data && !data->pin2.len) { + rv = iasecc_chv_change_pinpad(card, reference, tries_left); + sc_log(ctx, "iasecc_pin_cmd(SC_PIN_CMD_CHANGE) chv_change_pinpad returned %i", rv); + LOG_FUNC_RETURN(ctx, rv); + } + } + + if (!data->pin1.data && data->pin1.len) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid PIN1 arguments"); + + if (!data->pin2.data && data->pin2.len) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid PIN2 arguments"); + + rv = iasecc_pin_verify(card, data->pin_type, reference, data->pin1.data, data->pin1.len, tries_left); + sc_log(ctx, "iasecc_pin_cmd(SC_PIN_CMD_CHANGE) pin_verify returned %i", rv); + LOG_TEST_RET(ctx, rv, "PIN verification error"); + + if (data->pin1.len + data->pin2.len > sizeof(pin_data)) + LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Buffer too small for the 'Change PIN' data"); + + if (data->pin1.data) + memcpy(pin_data, data->pin1.data, data->pin1.len); + if (data->pin2.data) + memcpy(pin_data + data->pin1.len, data->pin2.data, data->pin2.len); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0, reference); + apdu.data = pin_data; + apdu.datalen = data->pin1.len + data->pin2.len; + apdu.lc = apdu.datalen; + + break; + case SC_PIN_CMD_UNBLOCK: + if (data->pin_type != SC_AC_CHV) { + sc_log(ctx, "To unblock PIN it's CHV reference should be presented"); + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + } + + rv = iasecc_pin_reset(card, data, tries_left); + LOG_TEST_RET(ctx, rv, "PIN unblock error"); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + case SC_PIN_CMD_GET_INFO: + rv = iasecc_pin_get_policy(card, data); + LOG_FUNC_RETURN(ctx, rv); + default: + sc_log(ctx, "Other pin commands not supported yet: 0x%X", data->cmd); + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Non-supported PIN command"); + } + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "PIN cmd failed"); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_get_serialnr(struct sc_card *card, struct sc_serial_number *serial) +{ + struct sc_context *ctx = card->ctx; + struct sc_iin *iin = &card->serialnr.iin; + struct sc_apdu apdu; + unsigned char rbuf[0xC0]; + size_t ii, offs; + int rv; + int coucou; + + LOG_FUNC_CALLED(ctx); + if (card->serialnr.len) + goto end; + + memset(&card->serialnr, 0, sizeof(card->serialnr)); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB0, 0x80 | IASECC_SFI_EF_SN, 0); + apdu.le = sizeof(rbuf); + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "Get 'serial number' data failed"); + + if (rbuf[0] != ISO7812_PAN_SN_TAG) + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "serial number parse error"); + + iin->mii = (rbuf[2] >> 4) & 0x0F; + + iin->country = 0; + for (ii=5; ii<8; ii++) { + iin->country *= 10; + iin->country += (rbuf[ii/2] >> ((ii & 0x01) ? 0 : 4)) & 0x0F; + } + + iin->issuer_id = 0; + for (ii=8; ii<10; ii++) { + iin->issuer_id *= 10; + iin->issuer_id += (rbuf[ii/2] >> (ii & 0x01 ? 0 : 4)) & 0x0F; + } + + offs = rbuf[1] > 8 ? rbuf[1] - 8 : 0; + if (card->type == SC_CARD_TYPE_IASECC_SAGEM) { + /* 5A 0A 92 50 00 20 10 10 25 00 01 3F */ + /* 00 02 01 01 02 50 00 13 */ + for (ii=0; ii < rbuf[1] - offs; ii++) + *(card->serialnr.value + ii) = ((rbuf[ii + offs + 1] & 0x0F) << 4) + + ((rbuf[ii + offs + 2] & 0xF0) >> 4) ; + card->serialnr.len = ii; + } + else { + for (ii=0; ii < rbuf[1] - offs; ii++) + *(card->serialnr.value + ii) = rbuf[ii + offs + 2]; + card->serialnr.len = ii; + } + + do { + char txt[0x200]; + size_t ii; + + for (ii=0;iiserialnr.len;ii++) + sprintf(txt + ii*2, "%02X", *(card->serialnr.value + ii)); + + sc_log(ctx, "serial number '%s'; mii %i; country %i; issuer_id %li", txt, iin->mii, iin->country, iin->issuer_id); + } while(0); + +end: + if (serial) + memcpy(serial, &card->serialnr, sizeof(*serial)); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_sdo_put_data(struct sc_card *card, struct iasecc_sdo_update *update) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + int ii, rv; + + LOG_FUNC_CALLED(ctx); + if (update->magic != SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid SDO update data"); + + for(ii=0; update->fields[ii].tag && ii < IASECC_SDO_TAGS_UPDATE_MAX; ii++) { + unsigned char *encoded = NULL; + int encoded_len; + + encoded_len = iasecc_sdo_encode_update_field(ctx, update->sdo_class, update->sdo_ref, + &update->fields[ii], &encoded); + sc_log(ctx, "iasecc_sdo_put_data() encode[%i]; tag %X; encoded_len %i", ii, update->fields[ii].tag, encoded_len); + LOG_TEST_RET(ctx, encoded_len, "Cannot encode update data"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xDB, 0x3F, 0xFF); + apdu.data = encoded; + apdu.datalen = encoded_len; + apdu.lc = encoded_len; + apdu.flags |= SC_APDU_FLAGS_CHAINING; + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "SDO put data error"); + + free(encoded); + } + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_sdo_key_rsa_put_data(struct sc_card *card, struct iasecc_sdo_rsa_update *update) +{ + struct sc_context *ctx = card->ctx; + unsigned char scb; + int rv; + + LOG_FUNC_CALLED(ctx); + + if (update->sdo_prv_key) { + sc_log(ctx, "encode private rsa in %p", &update->update_prv); + rv = iasecc_sdo_encode_rsa_update(card->ctx, update->sdo_prv_key, update->p15_rsa, &update->update_prv); + LOG_TEST_RET(ctx, rv, "failed to encode update of RSA private key"); + } + + if (update->sdo_pub_key) { + sc_log(ctx, "encode public rsa in %p", &update->update_pub); + if (card->type == SC_CARD_TYPE_IASECC_SAGEM) { + if (update->sdo_pub_key->data.pub_key.cha.value) { + free(update->sdo_pub_key->data.pub_key.cha.value); + memset(&update->sdo_pub_key->data.pub_key.cha, 0, sizeof(update->sdo_pub_key->data.pub_key.cha)); + } + } + rv = iasecc_sdo_encode_rsa_update(card->ctx, update->sdo_pub_key, update->p15_rsa, &update->update_pub); + LOG_TEST_RET(ctx, rv, "failed to encode update of RSA public key"); + } + + if (update->sdo_prv_key) { + sc_log(ctx, "reference of the private key to store: %X", update->sdo_prv_key->sdo_ref); + + if (update->sdo_prv_key->docp.acls_contact.size == 0) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "extremely strange ... there is no ACLs"); + + scb = update->sdo_prv_key->docp.scbs[IASECC_ACLS_RSAKEY_PUT_DATA]; + sc_log(ctx, "'UPDATE PRIVATE RSA' scb 0x%X", scb); + + do { + unsigned all_conditions = scb & IASECC_SCB_METHOD_NEED_ALL ? 1 : 0; + + if ((scb & IASECC_SCB_METHOD_USER_AUTH) && !all_conditions) + break; + + if (scb & IASECC_SCB_METHOD_EXT_AUTH) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); + + if (scb & IASECC_SCB_METHOD_SM) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); + } while(0); + + rv = iasecc_sdo_put_data(card, &update->update_prv); + LOG_TEST_RET(ctx, rv, "failed to update of RSA private key"); + } + + if (update->sdo_pub_key) { + sc_log(ctx, "reference of the public key to store: %X", update->sdo_pub_key->sdo_ref); + + rv = iasecc_sdo_put_data(card, &update->update_pub); + LOG_TEST_RET(ctx, rv, "failed to update of RSA public key"); + } + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_sdo_tag_from_class(unsigned sdo_class) +{ + switch (sdo_class & ~IASECC_OBJECT_REF_LOCAL) { + case IASECC_SDO_CLASS_CHV: + return IASECC_SDO_CHV_TAG; + case IASECC_SDO_CLASS_RSA_PRIVATE: + return IASECC_SDO_PRVKEY_TAG; + case IASECC_SDO_CLASS_RSA_PUBLIC: + return IASECC_SDO_PUBKEY_TAG; + case IASECC_SDO_CLASS_SE: + return IASECC_SDO_CLASS_SE; + case IASECC_SDO_CLASS_KEYSET: + return IASECC_SDO_KEYSET_TAG; + } + + return -1; +} + + +static int +iasecc_sdo_get_tagged_data(struct sc_card *card, int sdo_tag, struct iasecc_sdo *sdo) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + unsigned char sbuf[0x100]; + size_t offs = sizeof(sbuf) - 1; + unsigned char rbuf[0x400]; + int rv; + + LOG_FUNC_CALLED(ctx); + + sbuf[offs--] = 0x80; + sbuf[offs--] = sdo_tag & 0xFF; + if ((sdo_tag >> 8) & 0xFF) + sbuf[offs--] = (sdo_tag >> 8) & 0xFF; + sbuf[offs] = sizeof(sbuf) - offs - 1; + offs--; + + sbuf[offs--] = sdo->sdo_ref & 0x9F; + sbuf[offs--] = sdo->sdo_class | IASECC_OBJECT_REF_LOCAL; + sbuf[offs--] = IASECC_SDO_TAG_HEADER; + + sbuf[offs] = sizeof(sbuf) - offs - 1; + offs--; + sbuf[offs--] = IASECC_SDO_TEMPLATE_TAG; + + sbuf[offs] = sizeof(sbuf) - offs - 1; + offs--; + sbuf[offs] = 0x4D; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xCB, 0x3F, 0xFF); + apdu.data = sbuf + offs; + apdu.datalen = sizeof(sbuf) - offs; + apdu.lc = sizeof(sbuf) - offs; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 0x100; + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "SDO get data error"); + + rv = iasecc_sdo_parse(card, apdu.resp, apdu.resplen, sdo); + LOG_TEST_RET(ctx, rv, "cannot parse SDO data"); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_sdo_get_data(struct sc_card *card, struct iasecc_sdo *sdo) +{ + struct sc_context *ctx = card->ctx; + int rv, sdo_tag; + + LOG_FUNC_CALLED(ctx); + + sdo_tag = iasecc_sdo_tag_from_class(sdo->sdo_class); + + rv = iasecc_sdo_get_tagged_data(card, sdo_tag, sdo); + /* When there is no public data 'GET DATA' returns error */ + if (rv != SC_ERROR_INCORRECT_PARAMETERS) + LOG_TEST_RET(ctx, rv, "cannot parse ECC SDO data"); + + rv = iasecc_sdo_get_tagged_data(card, IASECC_DOCP_TAG, sdo); + LOG_TEST_RET(ctx, rv, "cannot parse ECC DOCP data"); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_sdo_generate(struct sc_card *card, struct iasecc_sdo *sdo) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_sdo_update update_pubkey; + struct sc_apdu apdu; + unsigned char scb, sbuf[5], rbuf[0x400], exponent[3] = {0x01, 0x00, 0x01}; + int offs = 0, rv = SC_ERROR_NOT_SUPPORTED; + + LOG_FUNC_CALLED(ctx); + + if (sdo->sdo_class != IASECC_SDO_CLASS_RSA_PRIVATE) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "For a moment, only RSA_PRIVATE class can be accepted for the SDO generation"); + + if (sdo->docp.acls_contact.size == 0) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "iasecc_sdo_generate() Extremely strange ... there is no ACLs"); + + scb = sdo->docp.scbs[IASECC_ACLS_RSAKEY_GENERATE]; + sc_log(ctx, "'generate RSA key' SCB 0x%X", scb); + do { + unsigned all_conditions = scb & IASECC_SCB_METHOD_NEED_ALL ? 1 : 0; + + if (scb & IASECC_SCB_METHOD_USER_AUTH) { + if (!all_conditions) + break; + } + + if (scb & IASECC_SCB_METHOD_EXT_AUTH) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); + + if (scb & IASECC_SCB_METHOD_SM) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); + } while(0); + + memset(&update_pubkey, 0, sizeof(update_pubkey)); + update_pubkey.magic = SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA; + update_pubkey.sdo_class = IASECC_SDO_CLASS_RSA_PUBLIC; + update_pubkey.sdo_ref = sdo->sdo_ref; + + update_pubkey.fields[0].parent_tag = IASECC_SDO_PUBKEY_TAG; + update_pubkey.fields[0].tag = IASECC_SDO_PUBKEY_TAG_E; + update_pubkey.fields[0].value = exponent; + update_pubkey.fields[0].size = sizeof(exponent); + + rv = iasecc_sdo_put_data(card, &update_pubkey); + LOG_TEST_RET(ctx, rv, "iasecc_sdo_generate() update SDO public key failed"); + + offs = 0; + sbuf[offs++] = IASECC_SDO_TEMPLATE_TAG; + sbuf[offs++] = 0x03; + sbuf[offs++] = IASECC_SDO_TAG_HEADER; + sbuf[offs++] = IASECC_SDO_CLASS_RSA_PRIVATE | IASECC_OBJECT_REF_LOCAL; + sbuf[offs++] = sdo->sdo_ref & ~IASECC_OBJECT_REF_LOCAL; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x47, 0x00, 0x00); + apdu.data = sbuf; + apdu.datalen = offs; + apdu.lc = offs; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 0x100; + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "SDO get data error"); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_get_chv_reference_from_se(struct sc_card *card, int *se_reference) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_se_info se; + struct sc_crt crt; + int rv; + + LOG_FUNC_CALLED(ctx); + + if (!se_reference) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid arguments"); + + memset(&se, 0, sizeof(se)); + se.reference = *se_reference; + + rv = iasecc_se_get_info(card, &se); + LOG_TEST_RET(ctx, rv, "get SE info error"); + + memset(&crt, 0, sizeof(crt)); + crt.tag = IASECC_CRT_TAG_AT; + crt.usage = IASECC_UQB_AT_USER_PASSWORD; + + rv = iasecc_se_get_crt(card, &se, &crt); + LOG_TEST_RET(ctx, rv, "Cannot get 'USER PASSWORD' authentication template"); + + LOG_FUNC_RETURN(ctx, crt.refs[0]); +} + + +static int +iasecc_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_sdo *sdo = (struct iasecc_sdo *) ptr; + + switch (cmd) { + case SC_CARDCTL_GET_SERIALNR: + return iasecc_get_serialnr(card, (struct sc_serial_number *)ptr); + case SC_CARDCTL_IASECC_SDO_PUT_DATA: + sc_log(ctx, "CMD SC_CARDCTL_IASECC_SDO_PUT_DATA: sdo_class %X", sdo->sdo_class); + return iasecc_sdo_put_data(card, (struct iasecc_sdo_update *) ptr); + case SC_CARDCTL_IASECC_SDO_KEY_RSA_PUT_DATA: + sc_log(ctx, "CMD SC_CARDCTL_IASECC_SDO_KEY_RSA_PUT_DATA"); + return iasecc_sdo_key_rsa_put_data(card, (struct iasecc_sdo_rsa_update *) ptr); + case SC_CARDCTL_IASECC_SDO_GET_DATA: + sc_log(ctx, "CMD SC_CARDCTL_IASECC_SDO_GET_DATA: sdo_class %X", sdo->sdo_class); + return iasecc_sdo_get_data(card, (struct iasecc_sdo *) ptr); + case SC_CARDCTL_IASECC_SDO_GENERATE: + sc_log(ctx, "CMD SC_CARDCTL_IASECC_SDO_GET_DATA: sdo_class %X", sdo->sdo_class); + return iasecc_sdo_generate(card, (struct iasecc_sdo *) ptr); + case SC_CARDCTL_GET_SE_INFO: + sc_log(ctx, "CMD SC_CARDCTL_GET_SE_INFO: sdo_class %X", sdo->sdo_class); + return iasecc_se_get_info(card, (struct iasecc_se_info *) ptr); + case SC_CARDCTL_GET_CHV_REFERENCE_IN_SE: + sc_log(ctx, "CMD SC_CARDCTL_GET_CHV_REFERENCE_IN_SE"); + return iasecc_get_chv_reference_from_se(card, (int *)ptr); + case SC_CARDCTL_IASECC_GET_FREE_KEY_REFERENCE: + sc_log(ctx, "CMD SC_CARDCTL_IASECC_GET_FREE_KEY_REFERENCE"); + return iasecc_get_free_reference(card, (struct iasecc_ctl_get_free_reference *)ptr); + } + return SC_ERROR_NOT_SUPPORTED; +} + + +static int +iasecc_decipher(struct sc_card *card, + const unsigned char *in, size_t in_len, + unsigned char *out, size_t out_len) +{ + struct sc_context *ctx = card->ctx; + struct sc_apdu apdu; + unsigned char sbuf[0x200]; + unsigned char resp[SC_MAX_APDU_BUFFER_SIZE]; + size_t offs; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(card->ctx, "crgram_len %i; outlen %i", in_len, out_len); + if (!out || !out_len || in_len > SC_MAX_APDU_BUFFER_SIZE) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + offs = 0; + sbuf[offs++] = 0x81; + memcpy(sbuf + offs, in, in_len); + offs += in_len; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86); + apdu.flags |= SC_APDU_FLAGS_CHAINING; + apdu.data = sbuf; + apdu.datalen = offs; + apdu.lc = offs; + apdu.resp = resp; + apdu.resplen = sizeof(resp); + apdu.le = in_len - (in_len % 8); + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "Card returned error"); + + if (out_len > apdu.resplen) + out_len = apdu.resplen; + + memcpy(out, apdu.resp, out_len); + rv = out_len; + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_get_qsign_data (struct sc_context *ctx, const unsigned char *in, size_t in_len, + struct iasecc_qsign_data *out, unsigned hash_type) +{ + SHA_CTX sha; + SHA_LONG *hh[5] = { + &sha.h0, &sha.h1, &sha.h2, &sha.h3, &sha.h4 + }; + int jj, ii; + int hh_size = sizeof(SHA_LONG), hh_num = SHA_DIGEST_LENGTH / sizeof(SHA_LONG); + unsigned long len; + + LOG_FUNC_CALLED(ctx); + if (hash_type != SC_ALGORITHM_RSA_HASH_SHA1) + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + + SHA1_Init(&sha); + + if (!in || !in_len || !out) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + sc_log(ctx, "sc_pkcs15_get_qsign_data() input data length %i", in_len); + + for (ii=0; ii 64 ? 64 : in_len - ii; + SHA1_Update(&sha, in + ii, len); + ii += len; + } + + sha.Nl -= sha.Nl % 0x200; + + for (jj=0; jjpre_hash[jj*hh_size + ii] = ((*hh[jj] >> 8*(hh_size-1-ii)) & 0xFF); + out->pre_hash_size = SHA_DIGEST_LENGTH; + + for (ii=0; iicounter[ii] = (sha.Nh >> 8*(hh_size-1-ii)) &0xFF; + out->counter[hh_size+ii] = (sha.Nl >> 8*(hh_size-1-ii)) &0xFF; + } + + sc_log(ctx, "Pre SHA1:%s", sc_dump_hex(out->pre_hash, out->pre_hash_size)); + sc_log(ctx, "Pre counter:%s", sc_dump_hex(out->counter, sizeof(out->counter))); + + for (ii=0;iilast_block[ii] = (sha.data[ii/hh_size] >> 8*(hh_size-1-(ii%hh_size))) &0xFF; + out->last_block_size = sha.num; + + SHA1_Final(out->hash, &sha); + out->hash_size = SHA_DIGEST_LENGTH; + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_compute_signature_dst(struct sc_card *card, + const unsigned char *in, size_t in_len, unsigned char *out, size_t out_len) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_private_data *prv = (struct iasecc_private_data *) card->drv_data; + struct sc_security_env *env = &prv->security_env; + struct iasecc_qsign_data qsign_data; + struct sc_apdu apdu; + size_t offs = 0, hash_len = 0; + unsigned char sbuf[SC_MAX_APDU_BUFFER_SIZE]; + unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "iasecc_compute_signature_dst() input length %i", in_len); + if (env->operation != SC_SEC_OPERATION_SIGN) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "It's not SC_SEC_OPERATION_SIGN"); + else if (!(prv->key_size & 0x1E0) || (prv->key_size & ~0x1E0)) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid key size for SC_SEC_OPERATION_SIGN"); + + memset(&qsign_data, 0, sizeof(qsign_data)); + if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) { + rv = iasecc_get_qsign_data(card->ctx, in, in_len, &qsign_data, SC_ALGORITHM_RSA_HASH_SHA1); + } + else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA256) { + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "RSA_HASH_SHA256 not yet supported"); + rv = iasecc_get_qsign_data(card->ctx, in, in_len, &qsign_data, SC_ALGORITHM_RSA_HASH_SHA256); + } + else + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Need RSA_HASH_SHA1 or RSA_HASH_SHA256 algorithm"); + LOG_TEST_RET(ctx, rv, "Cannot get QSign data"); + + sc_log(ctx, "iasecc_compute_signature_dst() hash_len %i; key_size %i", hash_len, prv->key_size); + + memset(sbuf, 0, sizeof(sbuf)); + sbuf[offs++] = 0x90; + sbuf[offs++] = qsign_data.hash_size + 8; + memcpy(sbuf + offs, qsign_data.pre_hash, qsign_data.pre_hash_size); + offs += qsign_data.pre_hash_size; + memcpy(sbuf + offs, qsign_data.counter, sizeof(qsign_data.counter)); + offs += sizeof(qsign_data.counter); + + sbuf[offs++] = 0x80; + sbuf[offs++] = qsign_data.last_block_size; + memcpy(sbuf + offs, qsign_data.last_block, qsign_data.last_block_size); + offs += qsign_data.last_block_size; + + sc_log(ctx, "iasecc_compute_signature_dst() offs %i; OP(meth:%X,ref:%X)", offs, prv->op_method, prv->op_ref); + if (prv->op_method == SC_AC_SCB && (prv->op_ref & IASECC_SCB_METHOD_SM)) + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Not yet"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x90, 0xA0); + apdu.data = sbuf; + apdu.datalen = offs; + apdu.lc = offs; + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "Compute signature failed"); + + sc_log(ctx, "iasecc_compute_signature_dst() partial hash OK"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x2A, 0x9E, 0x9A); + apdu.resp = rbuf; + apdu.resplen = prv->key_size; + apdu.le = prv->key_size; + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "Compute signature failed"); + + sc_log(ctx, "iasecc_compute_signature_dst() DST resplen %i", apdu.resplen); + if (apdu.resplen > out_len) + LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Result buffer too small for the DST signature"); + + memcpy(out, apdu.resp, apdu.resplen); + + LOG_FUNC_RETURN(ctx, apdu.resplen); +} + + +static int +iasecc_compute_signature_at(struct sc_card *card, + const unsigned char *in, size_t in_len, unsigned char *out, size_t out_len) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_private_data *prv = (struct iasecc_private_data *) card->drv_data; + struct sc_security_env *env = &prv->security_env; + struct sc_apdu apdu; + size_t offs = 0, sz = 0; + unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE]; + int rv; + + LOG_FUNC_CALLED(ctx); + if (env->operation != SC_SEC_OPERATION_AUTHENTICATE) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "It's not SC_SEC_OPERATION_AUTHENTICATE"); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x00, 0x00); + apdu.datalen = in_len; + apdu.data = in; + apdu.lc = in_len; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 0x100; + + rv = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, rv, "APDU transmit failed"); + rv = sc_check_sw(card, apdu.sw1, apdu.sw2); + LOG_TEST_RET(ctx, rv, "Compute signature failed"); + + do { + if (offs + apdu.resplen > out_len) + LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Buffer too small to return signature"); + + memcpy(out + offs, rbuf, apdu.resplen); + offs += apdu.resplen; + + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) + break; + + if (apdu.sw1 == 0x61) { + sz = apdu.sw2 == 0x00 ? 0x100 : apdu.sw2; + rv = iso_ops->get_response(card, &sz, rbuf); + LOG_TEST_RET(ctx, rv, "Get response error"); + + apdu.resplen = rv; + } + else { + LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "Impossible error: SW1 is not 0x90 neither 0x61"); + } + + } while(rv > 0); + + LOG_FUNC_RETURN(ctx, offs); +} + + +static int +iasecc_compute_signature(struct sc_card *card, + const unsigned char *in, size_t in_len, unsigned char *out, size_t out_len) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_private_data *prv = (struct iasecc_private_data *) card->drv_data; + struct sc_security_env *env = &prv->security_env; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "inlen %i, outlen %i", in_len, out_len); + if (!card || !in || !out) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid compute signature arguments"); + + if (env->operation == SC_SEC_OPERATION_SIGN) + return iasecc_compute_signature_dst(card, in, in_len, out, out_len); + else if (env->operation == SC_SEC_OPERATION_AUTHENTICATE) + return iasecc_compute_signature_at(card, in, in_len, out, out_len); + + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); +} + +#if 0 +static int +iasecc_read_public_key(struct sc_card *card, unsigned type, void *data, + unsigned char **out, size_t *out_len) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_sdo sdo; + struct sc_pkcs15_bignum bn[2]; + struct sc_pkcs15_pubkey_rsa key; + unsigned ref, size; + int rv; + + LOG_FUNC_CALLED(ctx); + if (type != SC_ALGORITHM_RSA) + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + + ref = ((struct sc_pkcs15_pubkey_info *)data)->key_reference; + size = ((struct sc_pkcs15_pubkey_info *)data)-> modulus_length; + + sc_log(ctx, "read public kay(ref:%i;size:%i)", ref, size); + + memset(&sdo, 0, sizeof(sdo)); + sdo.sdo_class = IASECC_SDO_CLASS_RSA_PUBLIC; + sdo.sdo_ref = ref & ~IASECC_OBJECT_REF_LOCAL; + + rv = iasecc_sdo_get_data(card, &sdo); + LOG_TEST_RET(ctx, rv, "failed to read public key: cannot get RSA SDO data"); + + if (out) + *out = NULL; + if (out_len) + *out_len = 0; + + bn[0].data = (unsigned char *) malloc(sdo.data.pub_key.n.size); + if (!bn[0].data) + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "failed to read public key: cannot allocate modulus"); + bn[0].len = sdo.data.pub_key.n.size; + memcpy(bn[0].data, sdo.data.pub_key.n.value, sdo.data.pub_key.n.size); + + bn[1].data = (unsigned char *) malloc(sdo.data.pub_key.e.size); + if (!bn[1].data) + LOG_TEST_RET(ctx, SC_ERROR_OUT_OF_MEMORY, "failed to read public key: cannot allocate exponent"); + bn[1].len = sdo.data.pub_key.e.size; + memcpy(bn[1].data, sdo.data.pub_key.e.value, sdo.data.pub_key.e.size); + + key.modulus = bn[0]; + key.exponent = bn[1]; + + rv = sc_pkcs15_encode_pubkey_rsa(card->ctx, &key, out, out_len); + LOG_TEST_RET(ctx, rv, "failed to read public key: cannot encode RSA public key"); + + sc_log(ctx, "encoded public key: %s", sc_dump_hex(*out, *out_len)); + + if (bn[0].data) + free(bn[0].data); + if (bn[1].data) + free(bn[1].data); + + iasecc_sdo_free_fields(card, &sdo); + + SC_FUNC_RETURN(ctx, 1, rv); +} +#endif + +static int +iasecc_get_free_reference(struct sc_card *card, struct iasecc_ctl_get_free_reference *ctl_data) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_sdo *sdo = NULL; + int idx, rv; + + LOG_FUNC_CALLED(ctx); + + if ((ctl_data->key_size % 0x40) || ctl_data->index < 1 || (ctl_data->index > IASECC_OBJECT_REF_MAX)) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + sc_log(ctx, "get reference for key(index:%i,usage:%X,access:%X)", ctl_data->index, ctl_data->usage, ctl_data->access); + /* TODO: when looking for the slot for the signature keys, check also PSO_SIGNATURE ACL */ + for (idx = ctl_data->index; idx <= IASECC_OBJECT_REF_MAX; idx++) { + unsigned char sdo_tag[3] = { + IASECC_SDO_TAG_HEADER, IASECC_OBJECT_REF_LOCAL | IASECC_SDO_CLASS_RSA_PRIVATE, idx + }; + size_t sz; + + if (sdo) + iasecc_sdo_free(card, sdo); + + rv = iasecc_sdo_allocate_and_parse(card, sdo_tag, 3, &sdo); + LOG_TEST_RET(ctx, rv, "cannot parse SDO data"); + + rv = iasecc_sdo_get_data(card, sdo); + if (rv == SC_ERROR_DATA_OBJECT_NOT_FOUND) { + iasecc_sdo_free(card, sdo); + + sc_log(ctx, "found empty key slot %i", idx); + break; + } + else + LOG_TEST_RET(ctx, rv, "get new key reference failed"); + + sz = *(sdo->docp.size.value + 0) * 0x100 + *(sdo->docp.size.value + 1); + sc_log(ctx, "SDO(idx:%i) size %i; key_size %i", idx, sz, ctl_data->key_size); + + if (sz != ctl_data->key_size / 8) + continue; + + if (sdo->docp.non_repudiation.value) { + sc_log(ctx, "non repudiation flag %X", sdo->docp.non_repudiation.value[0]); + if ((ctl_data->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) && !(*sdo->docp.non_repudiation.value)) { + sc_log(ctx, "key index %i ignored: need non repudiation", idx); + continue; + } + + if (!(ctl_data->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) && *sdo->docp.non_repudiation.value) { + sc_log(ctx, "key index %i ignored: don't need non-repudiation", idx); + continue; + } + } + + if (ctl_data->access & SC_PKCS15_PRKEY_ACCESS_LOCAL) { + if (sdo->docp.scbs[IASECC_ACLS_RSAKEY_GENERATE] == IASECC_SCB_NEVER) { + sc_log(ctx, "key index %i ignored: GENERATE KEY not allowed", idx); + continue; + } + } + else { + if (sdo->docp.scbs[IASECC_ACLS_RSAKEY_PUT_DATA] == IASECC_SCB_NEVER) { + sc_log(ctx, "key index %i ignored: PUT DATA not allowed", idx); + continue; + } + } + + if ((ctl_data->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) && (ctl_data->usage & SC_PKCS15_PRKEY_USAGE_SIGN)) { + if (sdo->docp.scbs[IASECC_ACLS_RSAKEY_PSO_SIGN] == IASECC_SCB_NEVER) { + sc_log(ctx, "key index %i ignored: PSO SIGN not allowed", idx); + continue; + } + } + else { + if (ctl_data->usage & (SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP)) { + if (sdo->docp.scbs[IASECC_ACLS_RSAKEY_PSO_DECIPHER] == IASECC_SCB_NEVER) { + sc_log(ctx, "key index %i ignored: PSO DECIPHER not allowed", idx); + continue; + } + } + if (ctl_data->usage & SC_PKCS15_PRKEY_USAGE_SIGN) { + if (sdo->docp.scbs[IASECC_ACLS_RSAKEY_INTERNAL_AUTH] == IASECC_SCB_NEVER) { + sc_log(ctx, "key index %i ignored: INTERNAL AUTHENTICATE not allowed", idx); + continue; + } + } + } + + break; + } + + ctl_data->index = idx; + + if (idx > IASECC_OBJECT_REF_MAX) + LOG_FUNC_RETURN(ctx, SC_ERROR_DATA_OBJECT_NOT_FOUND); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static struct sc_card_driver * +sc_get_driver(void) +{ + struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); + + if (!iso_ops) + iso_ops = iso_drv->ops; + + iasecc_ops = *iso_ops; + + iasecc_ops.match_card = iasecc_match_card; + iasecc_ops.init = iasecc_init; + iasecc_ops.finish = iasecc_finish; + iasecc_ops.read_binary = iasecc_read_binary; + // write_binary: ISO7816 implementation works + // update_binary: ISO7816 implementation works + iasecc_ops.erase_binary = iasecc_erase_binary; + // resize_binary + // read_record: Untested + // write_record: Untested + // append_record: Untested + // update_record: Untested + iasecc_ops.select_file = iasecc_select_file; + // get_response: Untested + // get_challenge: ISO7816 implementation works + iasecc_ops.logout = iasecc_logout; + // restore_security_env + iasecc_ops.set_security_env = iasecc_set_security_env; + // decipher: Untested + iasecc_ops.decipher = iasecc_decipher; + iasecc_ops.compute_signature = iasecc_compute_signature; + iasecc_ops.create_file = iasecc_create_file; + iasecc_ops.delete_file = iasecc_delete_file; + // list_files + iasecc_ops.check_sw = iasecc_check_sw; + iasecc_ops.card_ctl = iasecc_card_ctl; + iasecc_ops.process_fci = iasecc_process_fci; + // construct_fci: Not needed + iasecc_ops.pin_cmd = iasecc_pin_cmd; + // get_data: + // put_data: Not implemented + // delete_record: Not implemented + + // iasecc_ops.read_public_key = iasecc_read_public_key; + + return &iasecc_drv; +} + +struct sc_card_driver * +sc_get_iasecc_driver(void) +{ + return sc_get_driver(); +} + +#endif /* ENABLE_OPENSSL */ diff --git a/src/libopensc/iasecc-sdo.c b/src/libopensc/iasecc-sdo.c new file mode 100644 index 00000000..b748b824 --- /dev/null +++ b/src/libopensc/iasecc-sdo.c @@ -0,0 +1,1224 @@ +/* + * iasecc-sdo.c: library to manipulate the Security Data Objects (SDO) + * used by IAS/ECC card support. + * + * Copyright (C) 2010 Viktor Tarasov + * OpenTrust + * + * 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 + +#ifdef ENABLE_OPENSSL /* empty file without openssl */ + +#include +#include + +#include "internal.h" +#include "asn1.h" +#include "cardctl.h" + +#include "iasecc.h" +#include "iasecc-sdo.h" + +static int iasecc_parse_size(unsigned char *data, size_t *out); + + +int +iasecc_sdo_convert_acl(struct sc_card *card, struct iasecc_sdo *sdo, + unsigned char op, unsigned *out_method, unsigned *out_ref) +{ + struct sc_context *ctx = card->ctx; + struct acl_op { + unsigned char op; + unsigned char mask; + } ops[] = { + {SC_AC_OP_PSO_COMPUTE_SIGNATURE,IASECC_ACL_PSO_SIGNATURE}, + {SC_AC_OP_INTERNAL_AUTHENTICATE,IASECC_ACL_INTERNAL_AUTHENTICATE}, + {SC_AC_OP_PSO_DECRYPT, IASECC_ACL_PSO_DECIPHER}, + {SC_AC_OP_GENERATE, IASECC_ACL_GENERATE_KEY}, + {SC_AC_OP_UPDATE, IASECC_ACL_PUT_DATA}, + {SC_AC_OP_READ, IASECC_ACL_GET_DATA}, + {0x00, 0x00} + }; + unsigned char mask = 0x80, op_mask; + int ii; + + LOG_FUNC_CALLED(ctx); + + for (ii=0; ops[ii].mask; ii++) { + if (op == ops[ii].op) { + op_mask = ops[ii].mask; + break; + } + } + if (ops[ii].mask == 0) + LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + + *out_method = SC_AC_NEVER; + *out_ref = SC_AC_NEVER; + + for (ii=0; ii<7; ii++) { + mask >>= 1; + if (sdo->docp.amb & mask) { + if (op_mask == mask) { + unsigned char scb = sdo->docp.scbs[ii]; + + *out_ref = scb & 0x0F; + if (scb == 0) + *out_method = SC_AC_NONE; + else if (scb == 0xFF) + *out_method = SC_AC_NEVER; + else if ((scb & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_USER_AUTH) + *out_method = SC_AC_SEN; + else if ((scb & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_EXT_AUTH) + *out_method = SC_AC_AUT; + else if ((scb & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_SM) + *out_method = SC_AC_PRO; + else + *out_method = SC_AC_SCB, *out_ref = scb; + + break; + } + } + } + + sc_log(ctx, "returns method %X; ref %X", *out_method, *out_ref); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +void +iasecc_sdo_free_fields(struct sc_card *card, struct iasecc_sdo *sdo) +{ + if (sdo->docp.tries_remaining.value) + free(sdo->docp.tries_remaining.value); + if (sdo->docp.usage_remaining.value) + free(sdo->docp.usage_remaining.value); + if (sdo->docp.non_repudiation.value) + free(sdo->docp.non_repudiation.value); + if (sdo->docp.acls_contact.value) + free(sdo->docp.acls_contact.value); + if (sdo->docp.size.value) + free(sdo->docp.size.value); + if (sdo->docp.name.value) + free(sdo->docp.name.value); + if (sdo->docp.issuer_data.value) + free(sdo->docp.issuer_data.value); + + if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PUBLIC) { + if (sdo->data.pub_key.n.value) + free(sdo->data.pub_key.n.value); + if (sdo->data.pub_key.e.value) + free(sdo->data.pub_key.e.value); + if (sdo->data.pub_key.compulsory.value) + free(sdo->data.pub_key.compulsory.value); + if (sdo->data.pub_key.chr.value) + free(sdo->data.pub_key.chr.value); + if (sdo->data.pub_key.cha.value) + free(sdo->data.pub_key.cha.value); + } + else if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PRIVATE) { + if (sdo->data.prv_key.p.value) + free(sdo->data.prv_key.p.value); + if (sdo->data.prv_key.q.value) + free(sdo->data.prv_key.q.value); + if (sdo->data.prv_key.iqmp.value) + free(sdo->data.prv_key.iqmp.value); + if (sdo->data.prv_key.dmp1.value) + free(sdo->data.prv_key.dmp1.value); + if (sdo->data.prv_key.dmq1.value) + free(sdo->data.prv_key.dmq1.value); + if (sdo->data.prv_key.compulsory.value) + free(sdo->data.prv_key.compulsory.value); + } + else if (sdo->sdo_class == IASECC_SDO_CLASS_CHV) { + if (sdo->data.chv.size_max.value) + free(sdo->data.chv.size_max.value); + if (sdo->data.chv.size_min.value) + free(sdo->data.chv.size_min.value); + if (sdo->data.chv.value.value) + free(sdo->data.chv.value.value); + } +} + +void +iasecc_sdo_free(struct sc_card *card, struct iasecc_sdo *sdo) +{ + iasecc_sdo_free_fields(card, sdo); + free(sdo); +} + + +static int +iasecc_crt_parse(struct sc_card *card, unsigned char *data, struct iasecc_se_info *se) +{ + struct sc_context *ctx = card->ctx; + struct sc_crt crt; + int ii, offs, len, parsed_len = -1; + + sc_log(ctx, "iasecc_crt_parse(0x%X) called", *data); + + memset(&crt, 0, sizeof(crt)); + crt.tag = *(data + 0); + len = *(data + 1); + + for(offs = 2; offs < len + 2; offs += 3) { + sc_log(ctx, "iasecc_crt_parse(0x%X) CRT %X -> %X", *data, *(data + offs), *(data + offs + 2)); + if (*(data + offs) == IASECC_CRT_TAG_USAGE) { + crt.usage = *(data + offs + 2); + } + else if (*(data + offs) == IASECC_CRT_TAG_REFERENCE) { + int nn_refs = sizeof(crt.refs) / sizeof(crt.refs[0]); + + for (ii=0; iicrts[ii].tag) + break; + + if (ii==IASECC_SE_CRTS_MAX) + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_crt_parse() error: too much CRTs in SE"); + + memcpy(&se->crts[ii], &crt, sizeof(crt)); + parsed_len = len + 2; + LOG_FUNC_RETURN(ctx, parsed_len); +} + + +int +iasecc_se_get_crt(struct sc_card *card, struct iasecc_se_info *se, struct sc_crt *crt) +{ + struct sc_context *ctx = card->ctx; + int ii; + + LOG_FUNC_CALLED(ctx); + if (!se || !crt) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + sc_log(ctx, "CRT search template: %X:%X:%X, refs %X:%X:...", + crt->tag, crt->algo, crt->usage, crt->refs[0], crt->refs[1]); + + for (ii=0; iicrts[ii].tag; ii++) { + if (crt->tag != se->crts[ii].tag) + continue; + if (crt->algo && crt->algo != se->crts[ii].algo) + continue; + if (crt->usage && crt->usage != se->crts[ii].usage) + continue; + if (crt->refs[0] && crt->refs[0] != se->crts[ii].refs[0]) + continue; + + memcpy(crt, &se->crts[ii], sizeof(*crt)); + + sc_log(ctx, "iasecc_se_get_crt() found CRT with refs %X:%X:...", + se->crts[ii].refs[0], se->crts[ii].refs[1]); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + } + + sc_log(ctx, "iasecc_se_get_crt() CRT is not found"); + return SC_ERROR_DATA_OBJECT_NOT_FOUND; +} + + +int +iasecc_se_get_crt_by_usage(struct sc_card *card, struct iasecc_se_info *se, unsigned char tag, + unsigned char usage, struct sc_crt *crt) +{ + struct sc_context *ctx = card->ctx; + int ii; + + LOG_FUNC_CALLED(ctx); + if (!se || !crt || !tag || !usage) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + sc_log(ctx, "CRT search template with TAG:0x%X and UQB:0x%X", tag, usage); + + for (ii=0; iicrts[ii].tag; ii++) { + if (tag != se->crts[ii].tag) + continue; + if (usage != se->crts[ii].usage) + continue; + + memcpy(crt, &se->crts[ii], sizeof(*crt)); + + sc_log(ctx, "iasecc_se_get_crt() found CRT with refs %X:%X:...", crt->refs[0], crt->refs[1]); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + } + + sc_log(ctx, "iasecc_se_get_crt() CRT is not found"); + LOG_FUNC_RETURN(ctx, SC_ERROR_DATA_OBJECT_NOT_FOUND); +} + + +int +iasecc_se_parse(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_se_info *se) +{ + struct sc_context *ctx = card->ctx; + size_t size, offs, size_size; + int rv; + + LOG_FUNC_CALLED(ctx); + + if (*data == IASECC_SDO_TEMPLATE_TAG) { + size_size = iasecc_parse_size(data + 1, &size); + LOG_TEST_RET(ctx, size_size, "parse error: invalid size data of IASECC_SDO_TEMPLATE"); + + data += size_size + 1; + data_len = size; + sc_log(ctx, "IASECC_SDO_TEMPLATE: size %i, size_size %i", size, size_size); + + if (*data != IASECC_SDO_TAG_HEADER) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + + if ((*(data + 1) & 0x7F) != IASECC_SDO_CLASS_SE) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + + size_size = iasecc_parse_size(data + 3, &size); + LOG_TEST_RET(ctx, size_size, "parse error: invalid SDO SE data size"); + + if (data_len != size + size_size + 3) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalide SDO SE data size"); + + data += 3 + size_size; + data_len = size; + sc_log(ctx, "IASECC_SDO_TEMPLATE SE: size %i, size_size %i", size, size_size); + } + + if (*data != IASECC_SDO_CLASS_SE) { + sc_log(ctx, "Invalid SE tag 0x%X; data length %i", *data, data_len); + LOG_FUNC_RETURN(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } + + size_size = iasecc_parse_size(data + 1, &size); + LOG_TEST_RET(ctx, size_size, "parse error: invalid size data"); + + if (data_len != size + size_size + 1) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalide SE data size"); + + offs = 1 + size_size; + for (; offs < data_len;) { + rv = iasecc_crt_parse(card, data + offs, se); + LOG_TEST_RET(ctx, rv, "parse error: invalid SE data"); + + offs += rv; + } + + if (offs != data_len) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totaly parsed"); + + LOG_FUNC_RETURN(ctx, 0); +} + + +static int +iasecc_parse_size(unsigned char *data, size_t *out) +{ + if (*data < 0x80) { + *out = *data; + return 1; + } + else if (*data == 0x81) { + *out = *(data + 1); + return 2; + } + else if (*data == 0x82) { + *out = *(data + 1) * 0x100 + *(data + 2); + return 3; + } + + return SC_ERROR_INVALID_DATA; +} + + +static int +iasecc_parse_get_tlv(struct sc_card *card, unsigned char *data, struct iasecc_extended_tlv *tlv) +{ + struct sc_context *ctx = card->ctx; + size_t size_len, tag_len; + + memset(tlv, 0, sizeof(*tlv)); + sc_log(ctx, "iasecc_parse_get_tlv() called for tag 0x%X", *data); + if ((*data == 0x7F) || (*data == 0x5F)) { + tlv->tag = *data * 0x100 + *(data + 1); + tag_len = 2; + } + else { + tlv->tag = *data; + tag_len = 1; + } + + sc_log(ctx, "iasecc_parse_get_tlv() tlv->tag 0x%X", tlv->tag); + size_len = iasecc_parse_size(data + tag_len, &tlv->size); + LOG_TEST_RET(ctx, size_len, "parse error: invalid size data"); + + tlv->value = calloc(1, tlv->size); + if (!tlv->value) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + memcpy(tlv->value, data + size_len + tag_len, tlv->size); + + tlv->on_card = 1; + + sc_log(ctx, "iasecc_parse_get_tlv() parsed %i bytes", tag_len + size_len + tlv->size); + return tag_len + size_len + tlv->size; +} + + +static int +iasecc_parse_chv(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo_chv *chv) +{ + struct sc_context *ctx = card->ctx; + size_t offs = 0; + int rv; + + LOG_FUNC_CALLED(ctx); + while(offs < data_len) { + struct iasecc_extended_tlv tlv; + + rv = iasecc_parse_get_tlv(card, data + offs, &tlv); + LOG_TEST_RET(ctx, rv, "iasecc_parse_chv() get and parse TLV error"); + + sc_log(ctx, "iasecc_parse_chv() get and parse TLV returned %i; tag %X; size %i", rv, tlv.tag, tlv.size); + + if (tlv.tag == IASECC_SDO_CHV_TAG_SIZE_MAX) + chv->size_max = tlv; + else if (tlv.tag == IASECC_SDO_CHV_TAG_SIZE_MIN) + chv->size_min = tlv; + else if (tlv.tag == IASECC_SDO_CHV_TAG_VALUE) + chv->value = tlv; + else + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non CHV SDO tag"); + + offs += rv; + } + + LOG_FUNC_RETURN(ctx, 0); +} + + +static int +iasecc_parse_prvkey(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo_prvkey *prvkey) +{ + struct sc_context *ctx = card->ctx; + size_t offs = 0; + int rv; + + LOG_FUNC_CALLED(ctx); + while(offs < data_len) { + struct iasecc_extended_tlv tlv; + + rv = iasecc_parse_get_tlv(card, data + offs, &tlv); + LOG_TEST_RET(ctx, rv, "iasecc_parse_prvkey() get and parse TLV error"); + + sc_log(ctx, "iasecc_parse_prvkey() get and parse TLV returned %i; tag %X; size %i", rv, tlv.tag, tlv.size); + + if (tlv.tag == IASECC_SDO_PRVKEY_TAG_COMPULSORY) + prvkey->compulsory = tlv; + else + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non PrvKey SDO tag"); + + offs += rv; + } + + LOG_FUNC_RETURN(ctx, 0); +} + + +static int +iasecc_parse_pubkey(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo_pubkey *pubkey) +{ + struct sc_context *ctx = card->ctx; + size_t offs = 0; + int rv; + + LOG_FUNC_CALLED(ctx); + while(offs < data_len) { + struct iasecc_extended_tlv tlv; + + rv = iasecc_parse_get_tlv(card, data + offs, &tlv); + LOG_TEST_RET(ctx, rv, "iasecc_parse_pubkey() get and parse TLV error"); + + sc_log(ctx, "iasecc_parse_pubkey() get and parse TLV returned %i; tag %X; size %i", rv, tlv.tag, tlv.size); + + if (tlv.tag == IASECC_SDO_PUBKEY_TAG_N) + pubkey->n = tlv; + else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_E) + pubkey->e = tlv; + else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_CHR) + pubkey->chr = tlv; + else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_CHA) + pubkey->cha = tlv; + else if (tlv.tag == IASECC_SDO_PUBKEY_TAG_COMPULSORY) + pubkey->compulsory = tlv; + else + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non PubKey SDO tag"); + + offs += rv; + } + + LOG_FUNC_RETURN(ctx, 0); +} + + +static int +iasecc_parse_keyset(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo_keyset *keyset) +{ + struct sc_context *ctx = card->ctx; + size_t offs = 0; + int rv; + + LOG_FUNC_CALLED(ctx); + while(offs < data_len) { + struct iasecc_extended_tlv tlv; + + rv = iasecc_parse_get_tlv(card, data + offs, &tlv); + LOG_TEST_RET(ctx, rv, "iasecc_parse_keyset() get and parse TLV error"); + + sc_log(ctx, "iasecc_parse_prvkey() get and parse TLV returned %i; tag %X; size %i", rv, tlv.tag, tlv.size); + + if (tlv.tag == IASECC_SDO_KEYSET_TAG_COMPULSORY) + keyset->compulsory = tlv; + else + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "parse error: non KeySet SDO tag"); + + offs += rv; + } + + LOG_FUNC_RETURN(ctx, 0); +} + + +static int +iasecc_parse_docp(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo *sdo) +{ + struct sc_context *ctx = card->ctx; + size_t offs = 0; + int rv; + + LOG_FUNC_CALLED(ctx); + while(offs < data_len) { + struct iasecc_extended_tlv tlv; + + rv = iasecc_parse_get_tlv(card, data + offs, &tlv); + LOG_TEST_RET(ctx, rv, "iasecc_parse_get_tlv() get and parse TLV error"); + + sc_log(ctx, "iasecc_parse_docp() parse_get_tlv retuned %i; tag %X; size %i", rv, tlv.tag, tlv.size); + + if (tlv.tag == IASECC_DOCP_TAG_ACLS) { + int _rv = iasecc_parse_docp(card, tlv.value, tlv.size, sdo); + free(tlv.value); + LOG_TEST_RET(ctx, _rv, "parse error: cannot parse DOCP"); + } + else if (tlv.tag == IASECC_DOCP_TAG_ACLS_CONTACT) { + sdo->docp.acls_contact = tlv; + } + else if (tlv.tag == IASECC_DOCP_TAG_ACLS_CONTACTLESS) { + sdo->docp.acls_contactless = tlv; + } + else if (tlv.tag == IASECC_DOCP_TAG_SIZE) { + sdo->docp.size = tlv; + } + else if (tlv.tag == IASECC_DOCP_TAG_NAME) { + sdo->docp.name = tlv; + } + else if (tlv.tag == IASECC_DOCP_TAG_ISSUER_DATA) { + sdo->docp.issuer_data = tlv; + } + else if (tlv.tag == IASECC_DOCP_TAG_NON_REPUDATION) { + sdo->docp.non_repudiation = tlv; + } + else if (tlv.tag == IASECC_DOCP_TAG_USAGE_REMAINING) { + sdo->docp.usage_remaining = tlv; + } + else if (tlv.tag == IASECC_DOCP_TAG_TRIES_MAXIMUM) { + sdo->docp.tries_maximum = tlv; + } + else if (tlv.tag == IASECC_DOCP_TAG_TRIES_REMAINING) { + sdo->docp.tries_remaining = tlv; + } + else { + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_parse_get_tlv() parse error: non DOCP tag"); + } + + offs += rv; + } + + if (sdo->docp.acls_contact.size) { + int ii, offs; + unsigned char mask = 0x40; + + sdo->docp.amb = *(sdo->docp.acls_contact.value + 0); + memset(sdo->docp.scbs, 0xFF, sizeof(sdo->docp.scbs)); + for (ii=0, offs = 1; ii<7; ii++, mask >>= 1) + if (mask & sdo->docp.amb) + sdo->docp.scbs[ii] = *(sdo->docp.acls_contact.value + offs++); + + sc_log(ctx, "iasecc_parse_docp() SCBs %02X:%02X:%02X:%02X:%02X:%02X:%02X", + sdo->docp.scbs[0],sdo->docp.scbs[1],sdo->docp.scbs[2],sdo->docp.scbs[3], + sdo->docp.scbs[4],sdo->docp.scbs[5],sdo->docp.scbs[6]); + } + + LOG_FUNC_RETURN(ctx, 0); +} + + +static int +iasecc_sdo_parse_data(struct sc_card *card, unsigned char *data, struct iasecc_sdo *sdo) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_extended_tlv tlv; + int tlv_size, rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "iasecc_sdo_parse_data() class %X; ref %X", sdo->sdo_class, sdo->sdo_ref); + + tlv_size = iasecc_parse_get_tlv(card, data, &tlv); + LOG_TEST_RET(ctx, tlv_size, "parse error: get TLV"); + + sc_log(ctx, "iasecc_sdo_parse_data() tlv.tag 0x%X", tlv.tag); + if (tlv.tag == IASECC_DOCP_TAG) { + sc_log(ctx, "iasecc_sdo_parse_data() parse IASECC_DOCP_TAG: 0x%X; size %i", tlv.tag, tlv.size); + rv = iasecc_parse_docp(card, tlv.value, tlv.size, sdo); + sc_log(ctx, "iasecc_sdo_parse_data() parsed IASECC_DOCP_TAG rv %i", rv); + free(tlv.value); + LOG_TEST_RET(ctx, rv, "parse error: cannot parse DOCP"); + } + else if (tlv.tag == IASECC_DOCP_TAG_NON_REPUDATION) { + sdo->docp.non_repudiation = tlv; + } + else if (tlv.tag == IASECC_DOCP_TAG_USAGE_REMAINING) { + sdo->docp.usage_remaining = tlv; + } + else if (tlv.tag == IASECC_DOCP_TAG_TRIES_MAXIMUM) { + sdo->docp.tries_maximum = tlv; + } + else if (tlv.tag == IASECC_DOCP_TAG_TRIES_REMAINING) { + sdo->docp.tries_remaining = tlv; + } + else if (tlv.tag == IASECC_SDO_CHV_TAG) { + if (sdo->sdo_class != IASECC_SDO_CLASS_CHV) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: IASECC_SDO_CHV_TAG tag in non User CHV SDO"); + + rv = iasecc_parse_chv(card, tlv.value, tlv.size, &sdo->data.chv); + LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO CHV data"); + + free(tlv.value); + } + else if (tlv.tag == IASECC_SDO_PUBKEY_TAG) { + if (sdo->sdo_class != IASECC_SDO_CLASS_RSA_PUBLIC) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: SDO_PUBLIC_KEY tag in non PUBLIC_KEY SDO"); + + rv = iasecc_parse_pubkey(card, tlv.value, tlv.size, &sdo->data.pub_key); + LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO PUBLIC KEY data"); + + free(tlv.value); + } + else if (tlv.tag == IASECC_SDO_PRVKEY_TAG) { + if (sdo->sdo_class != IASECC_SDO_CLASS_RSA_PRIVATE) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: SDO_PRIVATE_KEY tag in non PRIVATE_KEY SDO"); + + rv = iasecc_parse_prvkey(card, tlv.value, tlv.size, &sdo->data.prv_key); + LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO PRIVATE KEY data"); + + free(tlv.value); + } + else if (tlv.tag == IASECC_SDO_KEYSET_TAG) { + if (sdo->sdo_class != IASECC_SDO_CLASS_KEYSET) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: SDO_KEYSET tag in non KEYSET SDO"); + + rv = iasecc_parse_keyset(card, tlv.value, tlv.size, &sdo->data.keyset); + LOG_TEST_RET(ctx, rv, "parse error: cannot parse SDO KEYSET data"); + + free(tlv.value); + } + else { + sc_log(ctx, "iasecc_sdo_parse_data() non supported tag 0x%X", tlv.tag); + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + } + + return tlv_size; +} + + +int +iasecc_sdo_parse(struct sc_card *card, unsigned char *data, size_t data_len, struct iasecc_sdo *sdo) +{ + struct sc_context *ctx = card->ctx; + size_t size, offs, size_size; + int rv; + + LOG_FUNC_CALLED(ctx); + + if (*data == IASECC_SDO_TEMPLATE_TAG) { + size_size = iasecc_parse_size(data + 1, &size); + LOG_TEST_RET(ctx, size_size, "parse error: invalid size data of IASECC_SDO_TEMPLATE"); + + data += size_size + 1; + data_len = size; + sc_log(ctx, "IASECC_SDO_TEMPLATE: size %i, size_size %i", size, size_size); + } + + if (*data != IASECC_SDO_TAG_HEADER) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + + if (sdo->sdo_class != (*(data + 1) & 0x7F)) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + + if (sdo->sdo_ref != (*(data + 2) & 0x3F)) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + + size_size = iasecc_parse_size(data + 3, &size); + LOG_TEST_RET(ctx, size_size, "parse error: invalid size data"); + + if (data_len != size + size_size + 3) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalide SDO data size"); + + sc_log(ctx, "sz %i, sz_size %i", size, size_size); + + offs = 3 + size_size; + for (; offs < data_len;) { + rv = iasecc_sdo_parse_data(card, data + offs, sdo); + LOG_TEST_RET(ctx, rv, "parse error: invalid SDO data"); + + offs += rv; + } + + if (offs != data_len) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totaly parsed"); + + sc_log(ctx, "docp.acls_contact.size %i, docp.size.size %i", sdo->docp.acls_contact.size, sdo->docp.size.size); + + LOG_FUNC_RETURN(ctx, 0); +} + + +int +iasecc_sdo_allocate_and_parse(struct sc_card *card, unsigned char *data, size_t data_len, + struct iasecc_sdo **out) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_sdo *sdo = NULL; + size_t size, offs, size_size; + int rv; + + LOG_FUNC_CALLED(ctx); + + if (*data != IASECC_SDO_TAG_HEADER) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + + if (data_len < 3) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + + sdo = calloc(1, sizeof(struct iasecc_sdo)); + if (!sdo) + return SC_ERROR_MEMORY_FAILURE; + + sdo->sdo_class = *(data + 1) & 0x7F; + sdo->sdo_ref = *(data + 2) & 0x3F; + + sc_log(ctx, "sdo_class 0x%X, sdo_ref 0x%X", sdo->sdo_class, sdo->sdo_ref); + if (data_len == 3) { + *out = sdo; + LOG_FUNC_RETURN(ctx, 0); + } + + size_size = iasecc_parse_size(data + 3, &size); + LOG_TEST_RET(ctx, size_size, "parse error: invalid size data"); + + if (data_len != size + size_size + 3) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: invalide SDO data size"); + + sc_log(ctx, "sz %i, sz_size %i", size, size_size); + + offs = 3 + size_size; + for (; offs < data_len;) { + rv = iasecc_sdo_parse_data(card, data + offs, sdo); + LOG_TEST_RET(ctx, rv, "parse error: invalid SDO data"); + + offs += rv; + } + + if (offs != data_len) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "parse error: not totaly parsed"); + + sc_log(ctx, "docp.acls_contact.size %i; docp.size.size %i", sdo->docp.acls_contact.size, sdo->docp.size.size); + + *out = sdo; + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_update_blob(struct sc_card *card, struct iasecc_extended_tlv *tlv, + unsigned char **blob, size_t *blob_size) +{ + struct sc_context *ctx = card->ctx; + unsigned char *pp = NULL; + int offs = 0, sz = tlv->size + 2; + + if (tlv->size == 0) + LOG_FUNC_RETURN(ctx, 0); + + sz = tlv->size + 2; + + if (tlv->tag > 0xFF) + sz += 1; + + if (tlv->size > 0x7F && tlv->size < 0x100) + sz += 1; + else if (tlv->size >= 0x100) + sz += 2; + + pp = realloc(*blob, *blob_size + sz); + if (!pp) + LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); + + if (tlv->tag > 0xFF) + *(pp + *blob_size + offs++) = (tlv->tag >> 8) & 0xFF; + *(pp + *blob_size + offs++) = tlv->tag & 0xFF; + + if (tlv->size >= 0x100) { + *(pp + *blob_size + offs++) = 0x82; + *(pp + *blob_size + offs++) = (tlv->size >> 8) & 0xFF; + } + else if (tlv->size > 0x7F) { + *(pp + *blob_size + offs++) = 0x81; + } + *(pp + *blob_size + offs++) = tlv->size & 0xFF; + + memcpy(pp + *blob_size + offs, tlv->value, tlv->size); + + *blob_size += sz; + *blob = pp; + + return 0; +} + + +int +iasecc_encode_docp(struct sc_card *card, struct iasecc_sdo_docp *docp, unsigned char **out, size_t *out_len) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_extended_tlv tlv, tlv_st; + unsigned char *st_blob, *tmp_blob, *docp_blob; + size_t blob_size; + int rv; + + LOG_FUNC_CALLED(ctx); + if (!docp->acls_contact.size || (docp->size.size != 2)) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_DATA); + + memset(&tlv, 0, sizeof(tlv)); + memset(&tlv_st, 0, sizeof(tlv_st)); + + st_blob = NULL; + blob_size = 0; + rv = iasecc_update_blob(card, &docp->acls_contact, &st_blob, &blob_size); + LOG_TEST_RET(ctx, rv, "ECC: cannot add contact ACLs to blob"); + + rv = iasecc_update_blob(card, &docp->acls_contactless, &st_blob, &blob_size); + LOG_TEST_RET(ctx, rv, "ECC: cannot add contactless ACLs to blob"); + + tlv.tag = IASECC_DOCP_TAG_ACLS; + tlv.size = blob_size; + tlv.value = st_blob; + + tmp_blob = NULL; + blob_size = 0; + rv = iasecc_update_blob(card, &tlv, &tmp_blob, &blob_size); + LOG_TEST_RET(ctx, rv, "ECC: cannot add ACLs template to blob"); + + rv = iasecc_update_blob(card, &docp->name, &tmp_blob, &blob_size); + LOG_TEST_RET(ctx, rv, "ECC: cannot add NAME to blob"); + + rv = iasecc_update_blob(card, &docp->tries_maximum, &tmp_blob, &blob_size); + LOG_TEST_RET(ctx, rv, "ECC: cannot add TRIES MAXIMUM to blob"); + + rv = iasecc_update_blob(card, &docp->tries_remaining, &tmp_blob, &blob_size); + LOG_TEST_RET(ctx, rv, "ECC: cannot add TRIES REMAINING to blob"); + + rv = iasecc_update_blob(card, &docp->usage_maximum, &tmp_blob, &blob_size); + LOG_TEST_RET(ctx, rv, "ECC: cannot add USAGE MAXIMUM to blob"); + + rv = iasecc_update_blob(card, &docp->usage_remaining, &tmp_blob, &blob_size); + LOG_TEST_RET(ctx, rv, "ECC: cannot add USAGE REMAINING to blob"); + + rv = iasecc_update_blob(card, &docp->non_repudiation, &tmp_blob, &blob_size); + LOG_TEST_RET(ctx, rv, "ECC: cannot add NON REPUDATION to blob"); + + rv = iasecc_update_blob(card, &docp->size, &tmp_blob, &blob_size); + LOG_TEST_RET(ctx, rv, "ECC: cannot add SIZE to blob"); + + rv = iasecc_update_blob(card, &docp->issuer_data, &tmp_blob, &blob_size); + LOG_TEST_RET(ctx, rv, "ECC: cannot add IDATA to blob"); + + tlv.tag = IASECC_DOCP_TAG; + tlv.size = blob_size; + tlv.value = tmp_blob; + + docp_blob = NULL; + blob_size = 0; + rv = iasecc_update_blob(card, &tlv, &docp_blob, &blob_size); + LOG_TEST_RET(ctx, rv, "ECC: cannot add ACLs to blob"); + + free(tmp_blob); + + if (out && out_len) { + *out = docp_blob; + *out_len = blob_size; + } + + LOG_FUNC_RETURN(ctx, 0); +} + + +static unsigned +iasecc_sdo_encode_asn1_tag(unsigned in_tag) +{ + unsigned short_tag; + unsigned out_tag; + + for (short_tag = in_tag; short_tag > 0xFF; short_tag >>= 8) + ; + out_tag = in_tag; + switch (short_tag & SC_ASN1_TAG_CLASS) { + case SC_ASN1_TAG_APPLICATION: + out_tag |= SC_ASN1_APP; + break; + case SC_ASN1_TAG_CONTEXT: + out_tag |= SC_ASN1_CTX; + break; + case SC_ASN1_TAG_PRIVATE: + out_tag |= SC_ASN1_PRV; + break; + } + return out_tag; +} + + +int +iasecc_sdo_encode_update_field(struct sc_context *ctx, unsigned char sdo_class, unsigned char sdo_ref, + struct iasecc_extended_tlv *tlv, unsigned char **out) +{ + unsigned sdo_full_ref; + size_t out_len; + int rv; + + struct sc_asn1_entry c_asn1_field_value[2] = { + { "fieldValue", SC_ASN1_OCTET_STRING, 0, SC_ASN1_ALLOC, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + struct sc_asn1_entry c_asn1_sdo_field[2] = { + { "sdoField", SC_ASN1_STRUCT, 0, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + struct sc_asn1_entry c_asn1_class_data[2] = { + { "classData", SC_ASN1_STRUCT, 0, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + struct sc_asn1_entry c_asn1_update_data[2] = { + { "updateData", SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE | SC_ASN1_APP | SC_ASN1_CONS, 0, NULL, NULL }, + { NULL, 0, 0, 0, NULL, NULL } + }; + struct sc_asn1_entry asn1_field_value[4], asn1_sdo_field[2], asn1_class_data[2], asn1_update_data[2]; + + LOG_FUNC_CALLED(ctx); + + c_asn1_field_value[0].tag = iasecc_sdo_encode_asn1_tag(tlv->tag); + c_asn1_sdo_field[0].tag = iasecc_sdo_encode_asn1_tag(tlv->parent_tag) | SC_ASN1_CONS; + + sdo_full_ref = (sdo_ref&0x3F) + 0x100*(sdo_class | IASECC_OBJECT_REF_LOCAL) + 0x10000*IASECC_SDO_TAG_HEADER; + c_asn1_class_data[0].tag = iasecc_sdo_encode_asn1_tag(sdo_full_ref) | SC_ASN1_CONS; + + sc_copy_asn1_entry(c_asn1_field_value, asn1_field_value); + sc_copy_asn1_entry(c_asn1_sdo_field, asn1_sdo_field); + sc_copy_asn1_entry(c_asn1_class_data, asn1_class_data); + sc_copy_asn1_entry(c_asn1_update_data, asn1_update_data); + + sc_format_asn1_entry(asn1_field_value + 0, tlv->value, &tlv->size, 1); + sc_format_asn1_entry(asn1_sdo_field + 0, asn1_field_value, NULL, 1); + sc_format_asn1_entry(asn1_class_data + 0, asn1_sdo_field, NULL, 1); + sc_format_asn1_entry(asn1_update_data + 0, asn1_class_data, NULL, 1); + + rv = sc_asn1_encode(ctx, asn1_update_data, out, &out_len); + LOG_TEST_RET(ctx, rv, "Encode update data error"); + + sc_debug(ctx, SC_LOG_DEBUG_ASN1,"Data: %s", sc_dump_hex(tlv->value, tlv->size)); + sc_debug(ctx, SC_LOG_DEBUG_ASN1,"Encoded: %s", sc_dump_hex(*out, out_len)); + LOG_FUNC_RETURN(ctx, out_len); +} + + +int +iasecc_sdo_encode_rsa_update(struct sc_context *ctx, struct iasecc_sdo *sdo, + struct sc_pkcs15_prkey_rsa *rsa, + struct iasecc_sdo_update *sdo_update) +{ + LOG_FUNC_CALLED(ctx); + + sc_log(ctx, "iasecc_sdo_encode_rsa_update() SDO class %X", sdo->sdo_class); + memset(sdo_update, 0, sizeof(*sdo_update)); + if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PRIVATE) { + sc_log(ctx, "iasecc_sdo_encode_rsa_update(IASECC_SDO_CLASS_RSA_PRIVATE)"); + if (!rsa->p.len || !rsa->q.len || !rsa->iqmp.len || !rsa->dmp1.len || !rsa->dmq1.len) { +#if 0 + if (!rsa->encrypted_key.value || !rsa->encrypted_key.len) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "need all private RSA key components"); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +#else + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "need all private RSA key components"); +#endif + } + + sdo_update->magic = SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA; + sdo_update->sdo_ref = sdo->sdo_ref; + + sdo_update->sdo_class = IASECC_SDO_CLASS_RSA_PRIVATE; + + sdo_update->fields[0].parent_tag = IASECC_SDO_PRVKEY_TAG; + sdo_update->fields[0].tag = IASECC_SDO_PRVKEY_TAG_P; + sdo_update->fields[0].value = rsa->p.data; + sdo_update->fields[0].size = rsa->p.len; + + sdo_update->fields[1].parent_tag = IASECC_SDO_PRVKEY_TAG; + sdo_update->fields[1].tag = IASECC_SDO_PRVKEY_TAG_Q; + sdo_update->fields[1].value = rsa->q.data; + sdo_update->fields[1].size = rsa->q.len; + + sdo_update->fields[2].parent_tag = IASECC_SDO_PRVKEY_TAG; + sdo_update->fields[2].tag = IASECC_SDO_PRVKEY_TAG_IQMP; + sdo_update->fields[2].value = rsa->iqmp.data; + sdo_update->fields[2].size = rsa->iqmp.len; + + sdo_update->fields[3].parent_tag = IASECC_SDO_PRVKEY_TAG; + sdo_update->fields[3].tag = IASECC_SDO_PRVKEY_TAG_DMP1; + sdo_update->fields[3].value = rsa->dmp1.data; + sdo_update->fields[3].size = rsa->dmp1.len; + + sdo_update->fields[4].parent_tag = IASECC_SDO_PRVKEY_TAG; + sdo_update->fields[4].tag = IASECC_SDO_PRVKEY_TAG_DMQ1; + sdo_update->fields[4].value = rsa->dmq1.data; + sdo_update->fields[4].size = rsa->dmq1.len; + + // TODO: Activated for Oberthur -- check for others + sc_log(ctx, "prv_key.compulsory.on_card %i", sdo->data.prv_key.compulsory.on_card); + if (!sdo->data.prv_key.compulsory.on_card) { + if (sdo->data.prv_key.compulsory.value) { + sc_log(ctx, "sdo_prvkey->data.prv_key.compulsory.size %i", sdo->data.prv_key.compulsory.size); + sdo_update->fields[5].parent_tag = IASECC_SDO_PRVKEY_TAG; + sdo_update->fields[5].tag = IASECC_SDO_PRVKEY_TAG_COMPULSORY; + sdo_update->fields[5].value = sdo->data.prv_key.compulsory.value; + sdo_update->fields[5].size = sdo->data.prv_key.compulsory.size; + } + } + } + else if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PUBLIC) { + int indx = 0; + sc_log(ctx, "iasecc_sdo_encode_rsa_update(IASECC_SDO_CLASS_RSA_PUBLIC)"); + + sdo_update->magic = SC_CARDCTL_IASECC_SDO_MAGIC_PUT_DATA; + sdo_update->sdo_ref = sdo->sdo_ref; + sdo_update->sdo_class = sdo->sdo_class; + + if (rsa->exponent.len) { + sdo_update->fields[indx].parent_tag = IASECC_SDO_PUBKEY_TAG; + sdo_update->fields[indx].tag = IASECC_SDO_PUBKEY_TAG_E; + sdo_update->fields[indx].value = rsa->exponent.data; + sdo_update->fields[indx].size = rsa->exponent.len; + indx++; + } + + if (rsa->modulus.len) { + sdo_update->fields[indx].parent_tag = IASECC_SDO_PUBKEY_TAG; + sdo_update->fields[indx].tag = IASECC_SDO_PUBKEY_TAG_N; + sdo_update->fields[indx].value = rsa->modulus.data; + sdo_update->fields[indx].size = rsa->modulus.len; + indx++; + } + + if (sdo->data.pub_key.cha.value) { + sdo_update->fields[indx].parent_tag = IASECC_SDO_PUBKEY_TAG; + sdo_update->fields[indx].tag = IASECC_SDO_PUBKEY_TAG_CHA; + sdo_update->fields[indx].value = sdo->data.pub_key.cha.value; + sdo_update->fields[indx].size = sdo->data.pub_key.cha.size; + indx++; + } + + if (sdo->data.pub_key.chr.value) { + sdo_update->fields[indx].parent_tag = IASECC_SDO_PUBKEY_TAG; + sdo_update->fields[indx].tag = IASECC_SDO_PUBKEY_TAG_CHR; + sdo_update->fields[indx].value = sdo->data.pub_key.chr.value; + sdo_update->fields[indx].size = sdo->data.pub_key.chr.size; + indx++; + } + + /* For ECC card 'compulsory' flag should be already here */ + if (!sdo->data.pub_key.compulsory.on_card) { + if (sdo->data.pub_key.compulsory.value) { + sdo_update->fields[indx].parent_tag = IASECC_SDO_PUBKEY_TAG; + sdo_update->fields[indx].tag = IASECC_SDO_PUBKEY_TAG_COMPULSORY; + sdo_update->fields[indx].value = sdo->data.pub_key.compulsory.value; + sdo_update->fields[indx].size = sdo->data.pub_key.compulsory.size; + indx++; + } + } + } + else { + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + } + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +int +iasecc_sdo_parse_card_answer(struct sc_context *ctx, unsigned char *data, size_t data_len, + struct iasecc_sm_card_answer *out) +{ + int offs, have_mac = 0, have_status = 0; + size_t size, size_size; + + LOG_FUNC_CALLED(ctx); + if (!data || !data_len || !out) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + memset(out, 0, sizeof(*out)); + for (offs=0; offs sizeof(out->data)) + LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "iasecc_sm_decode_answer() unbelivable !!!"); + + memcpy(out->data, data + offs + size_size + 1, size); + out->data_len = size; + offs += 1 + size_size + size; + } + else if (*(data + offs) == IASECC_CARD_ANSWER_TAG_SW ) { + if (*(data + offs + 1) != 2) + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_sm_decode_answer() SW length not 2"); + out->sw = *(data + offs + 2) * 0x100 + *(data + offs + 3); + + memcpy(out->ticket, data + offs, 4); + + offs += 4; + have_status = 1; + } + else if (*(data + offs) == IASECC_CARD_ANSWER_TAG_MAC ) { + if (*(data + offs + 1) != 8) + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_sm_decode_answer() MAC length not 8"); + memcpy(out->mac, data + offs + 2, 8); + + memcpy(out->ticket + 4, data + offs, 10); + + offs += 10; + have_mac = 1; + } + else { + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_sm_decode_answer() invalid card answer tag"); + } + } + + if (!have_mac || !have_status) + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "iasecc_sm_decode_answer() absent MAC or SW "); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_tlv_copy(struct sc_context *ctx, struct iasecc_extended_tlv *in, struct iasecc_extended_tlv *out) +{ + if (!in || !out) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + memset(out, 0, sizeof(struct iasecc_extended_tlv)); + out->tag = in->tag; + out->parent_tag = in->parent_tag; + out->on_card = in->on_card; + if (in->value && in->size) { + out->value = calloc(1, in->size); + if (!out->value) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + + memcpy(out->value, in->value, in->size); + out->size = in->size; + } + + return SC_SUCCESS; +} + + +int +iasecc_docp_copy(struct sc_context *ctx, struct iasecc_sdo_docp *in, struct iasecc_sdo_docp *out) +{ + int rv; + + LOG_FUNC_CALLED(ctx); + if (!in || !out) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + memset(out, 0, sizeof(struct iasecc_sdo_docp)); + + rv = iasecc_tlv_copy(ctx, &in->name, &out->name); + LOG_TEST_RET(ctx, rv, "TLV copy error"); + + rv = iasecc_tlv_copy(ctx, &in->tries_maximum, &out->tries_maximum); + LOG_TEST_RET(ctx, rv, "TLV copy error"); + + rv = iasecc_tlv_copy(ctx, &in->tries_remaining, &out->tries_remaining); + LOG_TEST_RET(ctx, rv, "TLV copy error"); + + rv = iasecc_tlv_copy(ctx, &in->usage_maximum, &out->usage_maximum); + LOG_TEST_RET(ctx, rv, "TLV copy error"); + + rv = iasecc_tlv_copy(ctx, &in->usage_remaining, &out->usage_remaining); + LOG_TEST_RET(ctx, rv, "TLV copy error"); + + rv = iasecc_tlv_copy(ctx, &in->non_repudiation, &out->non_repudiation); + LOG_TEST_RET(ctx, rv, "TLV copy error"); + + rv = iasecc_tlv_copy(ctx, &in->size, &out->size); + LOG_TEST_RET(ctx, rv, "TLV copy error"); + + rv = iasecc_tlv_copy(ctx, &in->acls_contact, &out->acls_contact); + LOG_TEST_RET(ctx, rv, "TLV copy error"); + + rv = iasecc_tlv_copy(ctx, &in->acls_contactless, &out->acls_contactless); + LOG_TEST_RET(ctx, rv, "TLV copy error"); + + out->amb = in->amb; + memcpy(out->scbs, in->scbs, sizeof(out->scbs)); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + +#endif /* ENABLE_OPENSSL */ diff --git a/src/libopensc/iasecc-sdo.h b/src/libopensc/iasecc-sdo.h new file mode 100644 index 00000000..8c5222d8 --- /dev/null +++ b/src/libopensc/iasecc-sdo.h @@ -0,0 +1,309 @@ +/* + * iasecc-sdo.h: Support for IAS/ECC smart cards + * + * Copyright (C) 2010 Viktor Tarasov + * OpenTrust + * + * 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 + */ + +#ifndef SC_IASECC_SDO_H +#define SC_IASECC_SDO_H + +#include "types.h" + +#define IASECC_SDO_TAG_HEADER 0xBF + +#define IASECC_SDO_TEMPLATE_TAG 0x70 + +#define IASECC_DOCP_TAG 0xA0 +#define IASECC_DOCP_TAG_NAME 0x84 +#define IASECC_DOCP_TAG_TRIES_MAXIMUM 0x9A +#define IASECC_DOCP_TAG_TRIES_REMAINING 0x9B +#define IASECC_DOCP_TAG_USAGE_MAXIMUM 0x9C +#define IASECC_DOCP_TAG_USAGE_REMAINING 0x9D +#define IASECC_DOCP_TAG_NON_REPUDATION 0x9E +#define IASECC_DOCP_TAG_SIZE 0x80 +#define IASECC_DOCP_TAG_ACLS 0xA1 +#define IASECC_DOCP_TAG_ACLS_CONTACT 0x8C +#define IASECC_DOCP_TAG_ACLS_CONTACTLESS 0x9C +#define IASECC_DOCP_TAG_ISSUER_DATA_BER 0xA5 +#define IASECC_DOCP_TAG_ISSUER_DATA 0x85 + +#define IASECC_ACLS_CHV_CHANGE 0 +#define IASECC_ACLS_CHV_VERIFY 1 +#define IASECC_ACLS_CHV_RESET 2 +#define IASECC_ACLS_CHV_PUT_DATA 5 +#define IASECC_ACLS_CHV_GET_DATA 6 + +#define IASECC_ACLS_RSAKEY_PSO_SIGN 0 +#define IASECC_ACLS_RSAKEY_INTERNAL_AUTH 1 +#define IASECC_ACLS_RSAKEY_PSO_DECIPHER 2 +#define IASECC_ACLS_RSAKEY_GENERATE 3 +#define IASECC_ACLS_RSAKEY_PUT_DATA 5 +#define IASECC_ACLS_RSAKEY_GET_DATA 6 + +#define IASECC_SDO_CHV_TAG 0x7F41 +#define IASECC_SDO_CHV_TAG_SIZE_MAX 0x80 +#define IASECC_SDO_CHV_TAG_SIZE_MIN 0x81 +#define IASECC_SDO_CHV_TAG_VALUE 0x82 + +#define IASECC_SDO_PRVKEY_TAG 0x7F48 +#define IASECC_SDO_PRVKEY_TAG_P 0x92 +#define IASECC_SDO_PRVKEY_TAG_Q 0x93 +#define IASECC_SDO_PRVKEY_TAG_IQMP 0x94 +#define IASECC_SDO_PRVKEY_TAG_DMP1 0x95 +#define IASECC_SDO_PRVKEY_TAG_DMQ1 0x96 +#define IASECC_SDO_PRVKEY_TAG_COMPULSORY 0x80 + +#define IASECC_SDO_PUBKEY_TAG 0x7F49 +#define IASECC_SDO_PUBKEY_TAG_N 0x81 +#define IASECC_SDO_PUBKEY_TAG_E 0x82 +#define IASECC_SDO_PUBKEY_TAG_COMPULSORY 0x80 +#define IASECC_SDO_PUBKEY_TAG_CHR 0x5F20 +#define IASECC_SDO_PUBKEY_TAG_CHA 0x5F4C + +#define IASECC_SDO_KEYSET_TAG 0xA2 +#define IASECC_SDO_KEYSET_TAG_MAC 0x90 +#define IASECC_SDO_KEYSET_TAG_ENC 0x91 +#define IASECC_SDO_KEYSET_TAG_COMPULSORY 0x80 + +#define IASECC_SCB_METHOD_NEED_ALL 0x80 +#define IASECC_SCB_METHOD_MASK 0x70 +#define IASECC_SCB_METHOD_MASK_REF 0x0F +#define IASECC_SCB_METHOD_SM 0x40 +#define IASECC_SCB_METHOD_EXT_AUTH 0x20 +#define IASECC_SCB_METHOD_USER_AUTH 0x10 + +#define IASECC_SCB_NEVER 0xFF +#define IASECC_SCB_ALWAYS 0x00 + +#define IASECC_SDO_CLASS_CHV 0x01 +#define IASECC_SDO_CLASS_KEYSET 0x0A +#define IASECC_SDO_CLASS_RSA_PRIVATE 0x10 +#define IASECC_SDO_CLASS_RSA_PUBLIC 0x20 +#define IASECC_SDO_CLASS_SE 0x7B + +#define IASECC_CRT_TAG_AT 0xA4 +#define IASECC_CRT_TAG_CT 0xB8 +#define IASECC_CRT_TAG_CCT 0xB4 +#define IASECC_CRT_TAG_DST 0xB6 +#define IASECC_CRT_TAG_HT 0xAA +#define IASECC_CRT_TAG_KAT 0xA6 + +#define IASECC_CRT_TAG_USAGE 0x95 +#define IASECC_CRT_TAG_REFERENCE 0x83 +#define IASECC_CRT_TAG_ALGO 0x80 + +#define IASECC_ALGORITHM_SYMMETRIC 0x0C +#define IASECC_ALGORITHM_DH 0x0B +#define IASECC_ALGORITHM_RSA_PKCS 0x02 +#define IASECC_ALGORITHM_RSA_9796_2 0x01 +#define IASECC_ALGORITHM_RSA_PKCS_DECRYPT 0x0A +#define IASECC_ALGORITHM_SHA1 0x10 +#define IASECC_ALGORITHM_SHA2 0x40 + +#define IASECC_ALGORITHM_ROLE_AUTH 0x1C +#define IASECC_ALGORITHM_SYMMETRIC_SHA1 0x0C +#define IASECC_ALGORITHM_SYMMETRIC_SHA256 0x8C + +#define IASECC_UQB_AT_MUTUAL_AUTHENTICATION 0xC0 +#define IASECC_UQB_AT_EXTERNAL_AUTHENTICATION 0x80 +#define IASECC_UQB_AT_AUTHENTICATION 0x40 +#define IASECC_UQB_AT_USER_PASSWORD 0x08 +#define IASECC_UQB_AT_USER_BIOMETRIC 0x04 + +#define IASECC_UQB_DST_VERIFICATION 0x80 +#define IASECC_UQB_DST_COMPUTATION 0x40 + +#define IASECC_UQB_CT_ENCIPHERMENT 0x80 +#define IASECC_UQB_CT_DECIPHERMENT 0x40 +#define IASECC_UQB_CT_SM_RESPONSE 0x20 +#define IASECC_UQB_CT_SM_COMMAND 0x10 + +#define IASECC_UQB_CCT_VERIFICATION 0x80 +#define IASECC_UQB_CCT_COMPUTATION 0x40 +#define IASECC_UQB_CCT_SM_RESPONSE 0x20 +#define IASECC_UQB_CCT_SM_COMMAND 0x10 + +#define IASECC_UQB_KAT 0x80 + +#define IASECC_ACL_GET_DATA 0x01 +#define IASECC_ACL_PUT_DATA 0x02 +#define IASECC_ACL_GENERATE_KEY 0x08 +#define IASECC_ACL_PSO_DECIPHER 0x10 +#define IASECC_ACL_INTERNAL_AUTHENTICATE 0x20 +#define IASECC_ACL_PSO_SIGNATURE 0x40 + +#define IASECC_SDO_TAGS_UPDATE_MAX 16 + +#define IASECC_SE_CRTS_MAX 24 + +#define _MAKE_IASECC_SDO_MAGIC(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | ((d))) + +#define IASECC_SDO_MAGIC _MAKE_IASECC_SDO_MAGIC('E', 'C', 'S', 'D') +#define IASECC_SDO_MAGIC_UPDATE _MAKE_IASECC_SDO_MAGIC('E', 'C', 'U', 'D') +#define IASECC_SDO_MAGIC_UPDATE_RSA _MAKE_IASECC_SDO_MAGIC('E', 'C', 'U', 'R') + +#define IASECC_MAX_SCBS 7 +#define IASECC_MAX_CRTS_IN_SE 24 + +struct iasecc_extended_tlv { + unsigned tag; + unsigned parent_tag; + + unsigned char *value; + size_t size; + + unsigned on_card; +}; + +struct iasecc_sdo_docp { + struct iasecc_extended_tlv name; + struct iasecc_extended_tlv tries_maximum; + struct iasecc_extended_tlv tries_remaining; + struct iasecc_extended_tlv usage_maximum; + struct iasecc_extended_tlv usage_remaining; + struct iasecc_extended_tlv non_repudiation; + struct iasecc_extended_tlv size; + struct iasecc_extended_tlv acls_contact; + struct iasecc_extended_tlv acls_contactless; + struct iasecc_extended_tlv issuer_data; + + unsigned char amb, scbs[IASECC_MAX_SCBS]; +}; + +struct iasecc_sdo_chv { + struct iasecc_extended_tlv size_max; + struct iasecc_extended_tlv size_min; + struct iasecc_extended_tlv value; +}; + +struct iasecc_sdo_prvkey { + struct iasecc_extended_tlv p; + struct iasecc_extended_tlv q; + struct iasecc_extended_tlv iqmp; + struct iasecc_extended_tlv dmp1; + struct iasecc_extended_tlv dmq1; + struct iasecc_extended_tlv compulsory; +}; + +struct iasecc_sdo_pubkey { + struct iasecc_extended_tlv n; + struct iasecc_extended_tlv e; + struct iasecc_extended_tlv compulsory; + struct iasecc_extended_tlv chr; + struct iasecc_extended_tlv cha; +}; + +struct iasecc_sdo_keyset { + struct iasecc_extended_tlv mac; + struct iasecc_extended_tlv enc; + struct iasecc_extended_tlv compulsory; +}; + +struct iasecc_sdo { + unsigned char sdo_class; + unsigned char sdo_ref; + + unsigned int usage; + + struct iasecc_sdo_docp docp; + + union { + struct iasecc_sdo_chv chv; + struct iasecc_sdo_prvkey prv_key; + struct iasecc_sdo_pubkey pub_key; + struct iasecc_sdo_keyset keyset; + } data; + + unsigned not_on_card; + unsigned magic; +}; + +struct iasecc_sdo_update { + unsigned char sdo_class; + unsigned char sdo_ref; + + struct iasecc_extended_tlv fields[IASECC_SDO_TAGS_UPDATE_MAX]; + + unsigned char acl_method, acl_ref; + + unsigned magic; +}; + +struct iasecc_sdo_rsa_update { + struct iasecc_sdo *sdo_prv_key; + struct iasecc_sdo *sdo_pub_key; + struct sc_pkcs15_prkey_rsa *p15_rsa; + + struct iasecc_sdo_update update_prv; + struct iasecc_sdo_update update_pub; + + unsigned magic; +}; + +struct iasecc_se_info { + struct iasecc_sdo_docp docp; + int reference; + + struct sc_crt crts[SC_MAX_CRTS_IN_SE]; + + struct sc_file *df; + struct iasecc_se_info *next; + + unsigned magic; +}; + +struct iasecc_sm_card_answer { + unsigned char data[SC_MAX_APDU_BUFFER_SIZE]; + size_t data_len; + + unsigned sw; + + unsigned char mac[8]; + unsigned char ticket[14]; +}; + +struct iasecc_ctl_get_free_reference { + size_t key_size; + unsigned usage; + unsigned access; + int index; +}; + +enum IASECC_KEY_TYPE { + IASECC_SDO_CLASS_RSA_PRV = 0x10, + IASECC_SDO_CLASS_RSA_PUB = 0x20 +}; + +struct sc_card; +int iasecc_sdo_convert_acl(struct sc_card *card, struct iasecc_sdo *, unsigned char, unsigned *, unsigned *); +void iasecc_sdo_free_fields(struct sc_card *card, struct iasecc_sdo *); +void iasecc_sdo_free(struct sc_card *, struct iasecc_sdo *); +int iasecc_se_parse(struct sc_card *, unsigned char *, size_t, struct iasecc_se_info *); +int iasecc_sdo_parse(struct sc_card *, unsigned char *, size_t, struct iasecc_sdo *); +int iasecc_sdo_allocate_and_parse(struct sc_card *, unsigned char *, size_t, struct iasecc_sdo **); +int iasecc_encode_size(size_t, unsigned char *); +int iasecc_encode_docp(struct sc_card *, struct iasecc_sdo_docp *, unsigned char **, size_t *); +int iasecc_sdo_encode_update_field(struct sc_context *, unsigned char, unsigned char, + struct iasecc_extended_tlv *, unsigned char **); +int iasecc_se_get_crt(struct sc_card *, struct iasecc_se_info *, struct sc_crt *); +int iasecc_se_get_crt_by_usage(struct sc_card *, struct iasecc_se_info *, + unsigned char, unsigned char, struct sc_crt *); +int iasecc_sdo_encode_rsa_update(struct sc_context *, struct iasecc_sdo *, struct sc_pkcs15_prkey_rsa *, struct iasecc_sdo_update *); +int iasecc_sdo_parse_card_answer(struct sc_context *ctx, unsigned char *data, size_t data_len, struct iasecc_sm_card_answer *out); +int iasecc_docp_copy(struct sc_context *ctx, struct iasecc_sdo_docp *in, struct iasecc_sdo_docp *out); +#endif diff --git a/src/libopensc/iasecc.h b/src/libopensc/iasecc.h new file mode 100644 index 00000000..f9ffc0ec --- /dev/null +++ b/src/libopensc/iasecc.h @@ -0,0 +1,140 @@ +/* + * iasecc.h Support for IAS/ECC smart cards + * + * Copyright (C) 2010 Viktor Tarasov + * OpenTrust + * + * 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 + */ + +#ifndef _OPENSC_IASECC_H +#define _OPENSC_IASECC_H + +#include "errors.h" +#include "types.h" +#include "iasecc-sdo.h" + +#define ISO7812_PAN_SN_TAG 0x5A +#define ISO7812_PAN_LENGTH 0x0C + +#ifndef SHA256_DIGEST_LENGTH + #define SHA_DIGEST_LENGTH 20 + #define SHA256_DIGEST_LENGTH 32 +#endif + +#ifndef CKM_RSA_PKCS + #define CKM_RSA_PKCS 0x00000001 + #define CKM_SHA1_RSA_PKCS 0x00000006 + #define CKM_SHA256_RSA_PKCS 0x00000040 + #define CKM_SHA_1 0x00000220 + #define CKM_SHA256 0x00000250 +#endif + +#define IASECC_TITLE "IASECC" + +#define IASECC_FCP_TAG 0x62 +#define IASECC_FCP_TAG_SIZE 0x80 +#define IASECC_FCP_TAG_TYPE 0x82 +#define IASECC_FCP_TAG_FID 0x83 +#define IASECC_FCP_TAG_NAME 0x84 +#define IASECC_FCP_TAG_SFID 0x88 +#define IASECC_FCP_TAG_ACLS 0xA1 +#define IASECC_FCP_TAG_ACLS_CONTACT 0x8C + +#define IASECC_FCP_TYPE_EF 0x01 +#define IASECC_FCP_TYPE_DF 0x38 + +#define IASECC_OBJECT_REF_LOCAL 0x80 +#define IASECC_OBJECT_REF_GLOBAL 0x00 + +#define IASECC_OBJECT_REF_MIN 0x01 +#define IASECC_OBJECT_REF_MAX 0x1F + +#define IASECC_SE_REF_MIN 0x01 +#define IASECC_SE_REF_MAX 0x0F + +/* IAS/ECC interindustry data tags */ +#define IASECC_ATR_TAG_IO_BUFFER_SIZES 0xE0 + +#define IASECC_SFI_EF_DIR 0x1E +#define IASECC_SFI_EF_ATR 0x1D +#define IASECC_SFI_EF_SN 0x1C +#define IASECC_SFI_EF_DH 0x1B + +#define IASECC_READ_BINARY_LENGTH_MAX 0xE7 + +#define IASECC_PSO_HASH_TAG_PARTIAL 0x90 +#define IASECC_PSO_HASH_TAG_REMAINING 0x80 + +#define IASECC_CARD_ANSWER_TAG_DATA 0x87 +#define IASECC_CARD_ANSWER_TAG_SW 0x99 +#define IASECC_CARD_ANSWER_TAG_MAC 0x8E + +#define IASECC_SM_DO_TAG_TLE 0x97 +#define IASECC_SM_DO_TAG_TSW 0x99 +#define IASECC_SM_DO_TAG_TCC 0x8E +#define IASECC_SM_DO_TAG_TCG_ODD_INS 0x85 +#define IASECC_SM_DO_TAG_TCG_EVEN_INS 0x87 +#define IASECC_SM_DO_TAG_TCG 0x87 +#define IASECC_SM_DO_TAG_TBR 0x85 + +struct sc_security_env; + +typedef struct iasecc_qsign_data { + int hash_algo; + + unsigned char hash[SHA256_DIGEST_LENGTH]; + size_t hash_size; + + unsigned char pre_hash[SHA256_DIGEST_LENGTH]; + size_t pre_hash_size; + + unsigned char counter[8]; + + unsigned char last_block[64]; + size_t last_block_size; +} iasecc_qsign_data_t; + + +struct iasecc_version { + unsigned char ic_manufacturer; + unsigned char ic_type; + unsigned char os_version; + unsigned char iasecc_version; +}; + +struct iasecc_io_buffer_sizes { + size_t send; + size_t send_sc; + size_t recv; + size_t recv_sc; +}; + +struct iasecc_private_data { + struct iasecc_version version; + struct iasecc_io_buffer_sizes max_sizes; + + struct sc_security_env security_env; + size_t key_size; + unsigned op_method, op_ref; + + struct iasecc_se_info *se_info; +}; + + +int sm_iasecc_rsa_generate(struct sc_card *card, unsigned security_condition, + struct iasecc_sdo *sdo); + +#endif diff --git a/src/pkcs15init/ias_adele_admin1.profile b/src/pkcs15init/ias_adele_admin1.profile new file mode 100644 index 00000000..af5ae8ed --- /dev/null +++ b/src/pkcs15init/ias_adele_admin1.profile @@ -0,0 +1,187 @@ +# +# PKCS15 r/w profile for Oberthur cards +# +cardinfo { + label = "IAS"; + manufacturer = "IAS Gemalto"; + + max-pin-length = 4; + min-pin-length = 4; + pin-encoding = ascii-numeric; + pin-pad-char = 0xFF; +} + +pkcs15 { + # Put certificates into the CDF itself? + direct-certificates = no; + # Put the DF length into the ODF file? + encode-df-length = no; + # Have a lastUpdate field in the EF(TokenInfo)? + do-last-update = yes; +} + +option ecc { + macros { + odf-size = 96; + aodf-size = 300; + cdf-size = 3000; + prkdf-size = 6700; + pukdf-size = 2300; + dodf-size = 3000; + skdf-size = 3000; + } +} + + +# 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 = 4; + min-length = 4; + flags = 0x10; # initialized + reference = 1; +} +PIN so-pin { + auth-id = FF; + attempts = 5; + max-length = 4; + min-length = 4; + flags = 0xB2; + reference = 2 +} + +# Additional filesystem info. +# This is added to the file system info specified in the +# main profile. +filesystem { + DF MF { + ACL = *=CHV4; + path = 3F00; + type = DF; + + # This is the DIR file + EF DIR { + type = EF; + file-id = 2F00; + size = 128; + acl = *=NONE; + } + + # Here comes the application DF + + DF PKCS15-AppDF { + type = DF; + aid = E8:28:BD:08:0F:D2:50:00:00:04:01:01; + acl = *=NONE; + size = 5000; + + EF PKCS15-ODF { + file-id = 5031; + size = 96; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-TokenInfo { + file-id = 5032; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + } + + DF Adele-AppDF { + type = DF; + aid = D2:50:00:00:04:41:64:E8:6C:65:01:01; + acl = *=NONE; + size = 5000; + + EF PKCS15-AODF { + file-id = 7001; + size = 300; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-PrKDF { + file-id = 7002; + size = 6700; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-PuKDF { + file-id = 7004; + size = 2300; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-SKDF { + file-id = 7003; + size = 3000; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-CDF { + file-id = 7005; + size = 3000; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-DODF { + file-id = 7006; + size = 3000; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + template key-domain { + # Private RSA keys + BSO private-key { + ACL = *=NEVER; + ACL = SIGN=SCBx17, AUTHENTICATE=SCBx17, DECIPHER=SCBx17, GENERATE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + # Private DES keys + BSO private-des { + size = 24; # 192 bits + # READ acl used insted of DECIPHER/ENCIPHER/CHECKSUM + } + + # Private data + EF private-data { + file-id = F000; + size = 36; + ACL = *=NONE; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=SCBx17; + } + + # Certificate + EF certificate { + # for the profiles 'ADELE Admin. 1 & 2' + # file-id: auth: A001; sign: A002; encr: A003; + # + file-id = B000; + ACL = *=NEVER; + ACL = UPDATE=SCBx17, READ=NONE, DELETE=NONE; + } + + #Public Key + BSO public-key { + ACL = *=NEVER; + ACL = AUTHENTICATE=SCBx17, GENERATE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + # Public DES keys + BSO public-des { + size = 24; # 192 bits + ACL = *=NONE; + } + + # Public data + EF public-data { + file-id = D000; + ACL = *=NONE; + ACL = WRITE=SCBx17, UPDATE=SCBx17, DELETE=NONE; + } + } + } + } +} + diff --git a/src/pkcs15init/ias_adele_admin2.profile b/src/pkcs15init/ias_adele_admin2.profile new file mode 100644 index 00000000..a35fb1b1 --- /dev/null +++ b/src/pkcs15init/ias_adele_admin2.profile @@ -0,0 +1,183 @@ +# +# PKCS15 r/w profile for Oberthur cards +# +cardinfo { + label = "IAS"; + manufacturer = "IAS Gemalto"; + + max-pin-length = 4; + 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 = yes; # yes/no +} + +pkcs15 { + # Put certificates into the CDF itself? + direct-certificates = no; + # Put the DF length into the ODF file? + encode-df-length = no; + # Have a lastUpdate field in the EF(TokenInfo)? + do-last-update = yes; +} + +option ecc { + macros { + odf-size = 96; + aodf-size = 300; + cdf-size = 3000; + prkdf-size = 6700; + pukdf-size = 2300; + dodf-size = 3000; + skdf-size = 3000; + } +} + + +# 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 = 4; + min-length = 4; + flags = 0x10; # initialized + reference = 1; +} +PIN so-pin { + auth-id = FF; + attempts = 5; + max-length = 4; + min-length = 4; + flags = 0xB2; + reference = 2 +} + +# Additional filesystem info. +# This is added to the file system info specified in the +# main profile. +filesystem { + DF MF { + ACL = *=CHV4; + path = 3F00; + type = DF; + + # This is the DIR file + EF DIR { + type = EF; + file-id = 2F00; + size = 128; + acl = *=NONE; + } + + # Here comes the application DF + + DF PKCS15-AppDF { + type = DF; + aid = E8:28:BD:08:0F:D2:50:00:00:04:02:01; + acl = *=NONE; + size = 5000; + + EF PKCS15-ODF { + file-id = 5031; + size = 96; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-TokenInfo { + file-id = 5032; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-AODF { + file-id = 7001; + size = 300; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-PrKDF { + file-id = 7002; + size = 6700; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-PuKDF { + file-id = 7004; + size = 2300; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-SKDF { + file-id = 7003; + size = 3000; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-CDF { + file-id = 7005; + size = 3000; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-DODF { + file-id = 7006; + size = 3000; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + template key-domain { + # Private RSA keys + BSO private-key { + ACL = *=NEVER; + ACL = SIGN=SCBx17, AUTHENTICATE=SCBx17, DECIPHER=SCBx17, GENERATE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + # Private DES keys + BSO private-des { + size = 24; # 192 bits + # READ acl used insted of DECIPHER/ENCIPHER/CHECKSUM + } + + # Private data + EF private-data { + file-id = F000; + size = 36; + ACL = *=NONE; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=SCBx17; + } + + # Certificate + EF certificate { + # for the profiles 'ADELE Admin. 1 & 2' + # file-id: auth: A001; sign: A002; encr: A003; + file-id = B000; + ACL = *=NEVER; + ACL = UPDATE=SCBx17, READ=NONE, DELETE=NONE; + } + + #Public Key + BSO public-key { + ACL = *=NEVER; + ACL = AUTHENTICATE=SCBx17, GENERATE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + # Public DES keys + BSO public-des { + size = 24; # 192 bits + ACL = *=NONE; + } + + # Public data + EF public-data { + file-id = D000; + ACL = *=NONE; + ACL = WRITE=SCBx17, UPDATE=SCBx17, DELETE=NONE; + } + } + } + } +} + diff --git a/src/pkcs15init/ias_adele_common.profile b/src/pkcs15init/ias_adele_common.profile new file mode 100644 index 00000000..5a68f068 --- /dev/null +++ b/src/pkcs15init/ias_adele_common.profile @@ -0,0 +1,178 @@ +# +# PKCS15 r/w profile for Oberthur cards +# +cardinfo { + label = "IAS"; + manufacturer = "IAS Gemalto"; + + max-pin-length = 4; + min-pin-length = 4; + pin-encoding = ascii-numeric; + pin-pad-char = 0xFF; +} + +pkcs15 { + # Put certificates into the CDF itself? + direct-certificates = no; + # Put the DF length into the ODF file? + encode-df-length = no; + # Have a lastUpdate field in the EF(TokenInfo)? + do-last-update = yes; +} + +option ecc { + macros { + odf-size = 96; + aodf-size = 300; + cdf-size = 3000; + prkdf-size = 6700; + pukdf-size = 2300; + dodf-size = 3000; + skdf-size = 3000; + } +} + + +# 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 = 4; + min-length = 4; + flags = 0x10; # initialized + reference = 1; +} +PIN so-pin { + auth-id = FF; + attempts = 5; + max-length = 4; + min-length = 4; + flags = 0xB2; + reference = 2 +} + +# Additional filesystem info. +# This is added to the file system info specified in the +# main profile. +filesystem { + DF MF { + ACL = *=CHV4; + path = 3F00; + type = DF; + + # This is the DIR file + EF DIR { + type = EF; + file-id = 2F00; + size = 128; + acl = *=NONE; + } + + # Here comes the application DF + DF PKCS15-AppDF { + type = DF; + aid = E8:28:BD:08:0F:D2:50:00:00:04:03:01; + acl = *=NONE; + size = 5000; + + EF PKCS15-ODF { + file-id = 5031; + size = 96; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-TokenInfo { + file-id = 5032; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-AODF { + file-id = 7001; + size = 300; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-PrKDF { + file-id = 7002; + size = 6700; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-PuKDF { + file-id = 7004; + size = 2300; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-SKDF { + file-id = 7003; + size = 3000; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-CDF { + file-id = 7005; + size = 3000; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + EF PKCS15-DODF { + file-id = 7006; + size = 3000; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + template key-domain { + # Private RSA keys + BSO private-key { + ACL = *=NEVER; + ACL = SIGN=SCBx17, AUTHENTICATE=SCBx17, DECIPHER=SCBx17, GENERATE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + # Private DES keys + BSO private-des { + size = 24; # 192 bits + # READ acl used insted of DECIPHER/ENCIPHER/CHECKSUM + } + + # Private data + EF private-data { + file-id = F000; + size = 36; + ACL = *=NONE; + ACL = WRITE=SCBx17, UPDATE=SCBx17, READ=SCBx17; + } + + # Certificate + EF certificate { + # for the profiles 'ADELE Admin. 1 & 2' + # file-id: auth: A001; sign: A002; encr: A003; + file-id = B000; + ACL = *=NEVER; + ACL = UPDATE=SCBx17, READ=NONE, DELETE=NONE; + } + + #Public Key + BSO public-key { + ACL = *=NEVER; + ACL = AUTHENTICATE=SCBx17, GENERATE=SCBx17, UPDATE=SCBx17, READ=NONE; + } + + # Public DES keys + BSO public-des { + size = 24; # 192 bits + ACL = *=NONE; + } + + # Public data + EF public-data { + file-id = D000; + ACL = *=NONE; + ACL = WRITE=SCBx17, UPDATE=SCBx17, DELETE=NONE; + } + } + } + } +} + diff --git a/src/pkcs15init/iasecc.profile b/src/pkcs15init/iasecc.profile new file mode 100644 index 00000000..ee4077f9 --- /dev/null +++ b/src/pkcs15init/iasecc.profile @@ -0,0 +1,113 @@ +# +# PKCS15 r/w profile for Oberthur cards +# +cardinfo { + label = "IAS"; + manufacturer = "IAS Gemalto"; + + max-pin-length = 4; + min-pin-length = 4; + pin-encoding = ascii-numeric; + pin-pad-char = 0xFF; +} + +pkcs15 { + # Put certificates into the CDF itself? + direct-certificates = no; + # Put the DF length into the ODF file? + encode-df-length = no; + # Have a lastUpdate field in the EF(TokenInfo)? + do-last-update = yes; +} + +option ecc { + macros { + odf-size = 96; + aodf-size = 300; + cdf-size = 3000; + prkdf-size = 6700; + pukdf-size = 2300; + dodf-size = 3000; + skdf-size = 3000; + } +} + + +# 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 = 4; + min-length = 4; + flags = 0x10; # initialized + reference = 1; +} +PIN so-pin { + auth-id = FF; + attempts = 5; + max-length = 4; + min-length = 4; + flags = 0xB2; + reference = 2 +} + +# CHV5 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 = *=CHV4; + path = 3F00; + type = DF; + + # This is the DIR file + EF DIR { + type = EF; + file-id = 2F00; + size = 128; + acl = *=NONE; + } + + # Here comes the application DF + DF CIA-Adele-AppDF { + type = DF; + exclusive-aid = E8:28:BD:08:0F:D2:50:00:00:04:01:01; + profile-extention = "ias_adele_admin1"; + } + + DF AdeleAdmin2-AppDF { + type = DF; + exclusive-aid = E8:28:BD:08:0F:D2:50:00:00:04:02:01; + profile-extention = "ias_adele_admin2"; + } + + DF AdeleCommon-AppDF { + type = DF; + exclusive-aid = E8:28:BD:08:0F:D2:50:00:00:04:03:01; + profile-extention = "ias_adele_common"; + } + + DF ECCeID-AppDF { + type = DF; + exclusive-aid = E8:28:BD:08:0F:D2:50:45:43:43:2D:65:49:44; + profile-extention = "iasecc_admin_eid"; + } + + DF ECCGeneric-AppDF { + type = DF; + exclusive-aid = E8:28:BD:08:0F:D2:50:47:65:6E:65:72:69:63; + profile-extention = "iasecc_generic_pki"; + } + + DF ECCGenericOberthur-AppDF { + type = DF; + exclusive-aid = E8:28:BD:08:0F:F2:50:4F:54:20:41:57:50; + profile-extention = "iasecc_generic_oberthur"; + } + } +} + diff --git a/src/pkcs15init/iasecc_admin_eid.profile b/src/pkcs15init/iasecc_admin_eid.profile new file mode 100644 index 00000000..3eadaf21 --- /dev/null +++ b/src/pkcs15init/iasecc_admin_eid.profile @@ -0,0 +1,182 @@ +# +# PKCS15 r/w profile for Oberthur cards +# +cardinfo { + label = "ECC v1.0.1"; + manufacturer = "Gemalto"; + + max-pin-length = 4; + min-pin-length = 4; + pin-encoding = ascii-numeric; + pin-pad-char = 0xFF; +} + +pkcs15 { + # Put certificates into the CDF itself? + direct-certificates = no; + # Put the DF length into the ODF file? + encode-df-length = no; + # Have a lastUpdate field in the EF(TokenInfo)? + do-last-update = yes; +} + +option ecc { + macros { + odf-size = 96; + aodf-size = 300; + cdf-size = 3000; + prkdf-size = 6700; + pukdf-size = 2300; + dodf-size = 3000; + skdf-size = 3000; + } +} + + +# 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 = 4; + min-length = 4; + flags = 0x10; # initialized + reference = 1; +} +PIN so-pin { + auth-id = FF; + attempts = 5; + max-length = 4; + min-length = 4; + flags = 0xB2; + reference = 2 +} + +# CHV5 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 = *=CHV4; + path = 3F00; + type = DF; + + # This is the DIR file + EF DIR { + type = EF; + file-id = 2F00; + size = 128; + acl = *=NONE; + } + + # Here comes the application DF + + DF PKCS15-AppDF { + type = DF; + aid = E8:28:BD:08:0F:D2:50:45:43:43:2D:65:49:44; + acl = *=NONE; + size = 5000; + + EF PKCS15-ODF { + file-id = 5031; + size = 60; + ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; + } + + EF PKCS15-TokenInfo { + file-id = 5032; + size = 400; + ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; + } + + EF PKCS15-AODF { + file-id = 7001; + size = 225; + ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; + } + + EF PKCS15-PrKDF { + file-id = 7002; + size = 450; + ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; + } + + EF PKCS15-PuKDF { + file-id = 7004; + size = 450; + ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; + } + + EF PKCS15-SKDF { + file-id = 7003; + size = 450; + ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; + } + + EF PKCS15-CDF { + file-id = 7005; + size = 300; + ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; + } + + EF PKCS15-DODF { + file-id = 7006; + size = 650; + ACL = WRITE=SCBx44, UPDATE=SCBx44, READ=NONE; + } + + template key-domain { + + # Private RSA keys + BSO private-key { + ACL = *=NEVER; + ACL = SIGN=SCBx13, AUTHENTICATE=SCBx13, DECIPHER=SCBx13, GENERATE=SCBx44, UPDATE=SCBx44, READ=NONE; + } + + # Private DES keys + BSO private-des { + size = 24; # 192 bits + # READ acl used insted of DECIPHER/ENCIPHER/CHECKSUM + } + + # Private data + EF private-data { + file-id = E000; + size = 36; + ACL = *=NONE; + ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=SCBx13; + } + + # Certificate + EF certificate { + file-id = B000; + ACL = *=NEVER; + ACL = UPDATE=SCBx44, READ=NONE, DELETE=NONE; + } + + #Public Key + BSO public-key { + ACL = *=NEVER; + ACL = AUTHENTICATE=SCBx13, GENERATE=SCBx44, UPDATE=SCBx44, READ=NONE; + } + + # Public DES keys + BSO public-des { + size = 24; # 192 bits + ACL = *=NONE; + } + + # Public data + EF public-data { + file-id = D000; + ACL = *=NONE; + ACL = WRITE=IDAxC1, UPDATE=IDAxC1, DELETE=NONE; + } + } + } + } +} + diff --git a/src/pkcs15init/iasecc_generic_oberthur.profile b/src/pkcs15init/iasecc_generic_oberthur.profile new file mode 100644 index 00000000..9de1f543 --- /dev/null +++ b/src/pkcs15init/iasecc_generic_oberthur.profile @@ -0,0 +1,176 @@ +# +# PKCS15 r/w profile for Oberthur cards +# +cardinfo { + label = "IAS/ECC v1.0.1"; + manufacturer = "OpenSC/Oberthur"; + + max-pin-length = 4; + min-pin-length = 4; + pin-encoding = ascii-numeric; + pin-pad-char = 0xFF; +} + +pkcs15 { + # Put certificates into the CDF itself? + direct-certificates = no; + # Put the DF length into the ODF file? + encode-df-length = no; + # Have a lastUpdate field in the EF(TokenInfo)? + do-last-update = yes; +} + +option ecc { + macros { + odf-size = 96; + aodf-size = 300; + cdf-size = 3000; + prkdf-size = 6700; + pukdf-size = 2300; + dodf-size = 3000; + skdf-size = 3000; + } +} + + +# 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 = 4; + min-length = 4; + flags = 0x10; # initialized + reference = 0xC1; +} +PIN so-pin { + auth-id = FF; + attempts = 5; + max-length = 4; + min-length = 4; + flags = 0xB2; + reference = 2 +} + +# Additional filesystem info. +# This is added to the file system info specified in the +# main profile. +filesystem { + DF MF { + ACL = *=CHV4; + path = 3F00; + type = DF; + + # This is the DIR file + EF DIR { + type = EF; + file-id = 2F00; + size = 128; + acl = *=NONE; + } + + # Here comes the application DF + DF PKCS15-AppDF { + type = DF; + aid = E8:28:BD:08:0F:F2:50:4F:54:20:41:57:50; + acl = *=NONE; + size = 5000; + + EF PKCS15-ODF { + file-id = 5031; + ACL = *=NEVER; + ACL = READ=NONE; + } + + EF PKCS15-TokenInfo { + file-id = 5032; + ACL = *=NEVER; + ACL = READ=NONE; + } + + EF PKCS15-AODF { + file-id = 7001; + ACL = *=NEVER; + ACL = READ=NONE; + } + + EF PKCS15-PrKDF { + file-id = 7002; + ACL = *=NEVER; + ACL = WRITE=SCB0x12, UPDATE=SCB0x12, READ=NONE; + } + + EF PKCS15-PuKDF { + file-id = 7004; + ACL = *=NEVER; + ACL = WRITE=SCB0x12, UPDATE=SCB0x12, READ=NONE; + } + + EF PKCS15-SKDF { + file-id = 7003; + ACL = *=NEVER; + ACL = WRITE=SCB0x12, UPDATE=SCB0x12, READ=NONE; + } + + EF PKCS15-CDF { + file-id = 7005; + ACL = WRITE=SCB0x12, UPDATE=SCB0x12, READ=NONE; + } + + EF PKCS15-DODF { + file-id = 7006; + ACL = *=NEVER; + ACL = WRITE=SCB0x12, UPDATE=SCB0x12, READ=NONE; + } + + template key-domain { + # Private RSA keys + BSO private-key { + ACL = *=NEVER; + ACL = UPDATE=COUCOUx12; + ACL = UPDATE=SCB0x12, READ=NONE; + ACL = PSO-COMPUTE-SIGNATURE=SCB0x12, INTERNAL-AUTHENTICATE=SCB0x12, PSO-DECRYPT=SCB0x12, GENERATE=SCB0x12; + + } + + # Private DES keys + BSO private-des { + size = 24; # 192 bits + # READ acl used insted of DECIPHER/ENCIPHER/CHECKSUM + } + + # Private data + EF private-data { + file-id = E000; + ACL = *=NEVER; + ACL = WRITE=SCB0x12, UPDATE=SCB0x12, READ=SCB0x12; + } + # Certificate + EF certificate { + file-id = 3401; + ACL = *=NEVER; + ACL = UPDATE=SCB0x12, READ=NONE, DELETE=NONE; + } + + #Public Key + BSO public-key { + ACL = *=NEVER; + ACL = AUTHENTICATE=SCB0x12, GENERATE=SCB0x12, UPDATE=SCB0x12, READ=NONE; + } + + # Public DES keys + BSO public-des { + size = 24; # 192 bits + ACL = *=NONE; + } + + # Public data + EF public-data { + file-id = F000; + ACL = *=NONE; + } + } + } + } +} + diff --git a/src/pkcs15init/iasecc_generic_pki.profile b/src/pkcs15init/iasecc_generic_pki.profile new file mode 100644 index 00000000..d4a7d4ca --- /dev/null +++ b/src/pkcs15init/iasecc_generic_pki.profile @@ -0,0 +1,179 @@ +# +# PKCS15 r/w profile +# +cardinfo { + label = "IAS/ECC Generic PKI application"; + manufacturer = "IAS/ECC OpenSC"; + + max-pin-length = 4; + min-pin-length = 4; + pin-encoding = ascii-numeric; + pin-pad-char = 0xFF; +} + +pkcs15 { + # Put certificates into the CDF itself? + direct-certificates = no; + # Put the DF length into the ODF file? + encode-df-length = no; + # Have a lastUpdate field in the EF(TokenInfo)? + do-last-update = yes; +} + +option ecc { + macros { + odf-size = 96; + aodf-size = 300; + cdf-size = 3000; + prkdf-size = 6700; + pukdf-size = 2300; + dodf-size = 3000; + skdf-size = 3000; + } +} + + +# 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 = 4; + min-length = 4; + flags = 0x10; # initialized + reference = 0xC1; +} +PIN so-pin { + auth-id = FF; + attempts = 5; + max-length = 4; + min-length = 4; + flags = 0xB2; + reference = 2 +} + +# CHV5 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 = *=CHV4; + path = 3F00; + type = DF; + + # This is the DIR file + EF DIR { + type = EF; + file-id = 2F00; + size = 128; + acl = *=NONE; + } + + # Here comes the application DF + DF PKCS15-AppDF { + type = DF; + exclusive-aid = E8:28:BD:08:0F:D2:50:47:65:6E:65:72:69:63; + acl = *=NONE; + size = 5000; + + EF PKCS15-ODF { + file-id = 5031; + size = 96; + ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; + } + + EF PKCS15-TokenInfo { + file-id = 5032; + ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; + } + + EF PKCS15-AODF { + file-id = 7001; + size = 300; + ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; + } + + EF PKCS15-PrKDF { + file-id = 7002; + size = 6700; + ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; + } + + EF PKCS15-PuKDF { + file-id = 7004; + size = 2300; + ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; + } + + EF PKCS15-SKDF { + file-id = 7003; + size = 3000; + ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; + } + + EF PKCS15-CDF { + file-id = 7005; + size = 3000; + ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; + } + + EF PKCS15-DODF { + file-id = 7006; + size = 3000; + ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=NONE; + } + + template key-domain { + # Private RSA keys + BSO private-key { + ACL = *=NEVER; + ACL = UPDATE=SCBx13, READ=NONE; + ACL = PSO-DECRYPT=SCBx13, INTERNAL-AUTHENTICATE=SCBx13, GENERATE=SCBx13; + } + + # Private DES keys + BSO private-des { + size = 24; # 192 bits + # READ acl used insted of DECIPHER/ENCIPHER/CHECKSUM + } + + # Private data + EF private-data { + file-id = E000; + size = 36; + ACL = *=NONE; + ACL = WRITE=SCBx13, UPDATE=SCBx13, READ=SCBx13; + } + # Certificate + EF certificate { + file-id = B000; + ACL = *=NEVER; + ACL = UPDATE=SCBx13, READ=NONE, DELETE=NONE; + } + + #Public Key + BSO public-key { + ACL = *=NEVER; + ACL = INTERNAL-AUTHENTICATE=SCBx13, GENERATE=SCBx13, UPDATE=SCBx13, READ=NONE; + } + + # Public DES keys + BSO public-des { + size = 24; # 192 bits + ACL = *=NONE; + } + + # Public data + EF public-data { + file-id = F000; + ACL = *=NONE; + ACL = WRITE=SCBx13, UPDATE=SCBx13, DELETE=NONE; + } + } + } + } +} + diff --git a/src/pkcs15init/pkcs15-iasecc.c b/src/pkcs15init/pkcs15-iasecc.c new file mode 100644 index 00000000..df0d2330 --- /dev/null +++ b/src/pkcs15init/pkcs15-iasecc.c @@ -0,0 +1,1527 @@ +/* + * IAS/ECC specific operations for PKCS #15 initialization + * + * Copyright (C) 2002 Juha Yrjölä + * Copyright (C) 2010 Viktor Tarasov + * OpenTrust + * + * 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 + +#ifdef ENABLE_OPENSSL /* empty file without openssl */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../libopensc/opensc.h" +#include "../libopensc/cardctl.h" +#include "../libopensc/log.h" +#include "../libopensc/pkcs15.h" +#include "../libopensc/cards.h" +#include "../libopensc/iasecc.h" +#include "../libopensc/iasecc-sdo.h" + +#include "pkcs15-init.h" +#include "profile.h" + +#define IASECC_TITLE "IASECC" + +int iasecc_pkcs15_delete_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_file *df); + +void +iasecc_reference_to_pkcs15_id (unsigned int ref, struct sc_pkcs15_id *id) +{ + int ii, sz; + + for (ii=0, sz = 0; ii> 8*ii) + sz++; + + for (ii=0; ii < sz; ii++) + id->value[sz - ii - 1] = (ref >> 8*ii) & 0xFF; + + id->len = sz; +} + + +int +iasecc_pkcs15_delete_file(struct sc_pkcs15_card *p15card, struct sc_profile *profile, + struct sc_file *df) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_card *card = p15card->card; + struct sc_path path; + unsigned long caps = card->caps; + int rv = 0; + + LOG_FUNC_CALLED(ctx); + + sc_log(ctx, "iasecc_pkcs15_delete_file() id %04X\n", df->id); + + card->caps |= SC_CARD_CAP_USE_FCI_AC; + rv = sc_pkcs15init_authenticate(profile, p15card, df, SC_AC_OP_DELETE); + card->caps = caps; + + LOG_TEST_RET(ctx, rv, "Cannnot authenticate SC_AC_OP_DELETE"); + + 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); + LOG_FUNC_RETURN(ctx, rv); +} + + +/* + * Erase the card + * + */ +static int +iasecc_pkcs15_erase_card(struct sc_profile *profile, struct sc_pkcs15_card *p15card) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_file *file = NULL; + struct sc_path path; + struct sc_pkcs15_df *df; + int rv; + + LOG_FUNC_CALLED(ctx); + + if (p15card->app->ddo.aid.len) { + memset(&path, 0, sizeof(struct sc_path)); + path.type = SC_PATH_TYPE_DF_NAME; + memcpy(path.value, p15card->app->ddo.aid.value, p15card->app->ddo.aid.len); + path.len = p15card->app->ddo.aid.len; + + sc_log(ctx, "Select DDO AID: %s", sc_print_path(&path)); + rv = sc_select_file(p15card->card, &path, NULL); + LOG_TEST_RET(ctx, rv, "Erase application error: cannot select DDO AID"); + } + + for (df = p15card->df_list; df; df = df->next) { + struct sc_pkcs15_object *objs[32]; + unsigned obj_type = 0; + int ii; + + if (df->type == SC_PKCS15_PRKDF) + obj_type = SC_PKCS15_TYPE_PRKEY; + else if (df->type == SC_PKCS15_PUKDF) + obj_type = SC_PKCS15_TYPE_PUBKEY; + else if (df->type == SC_PKCS15_CDF) + obj_type = SC_PKCS15_TYPE_CERT; + else + continue; + + rv = sc_pkcs15_get_objects(p15card, obj_type, objs, 32); + LOG_TEST_RET(ctx, rv, "Failed to get PKCS#15 objects to remove"); + + for (ii=0; iicard, &df->path, &file); + if (rv == SC_ERROR_FILE_NOT_FOUND) + continue; + LOG_TEST_RET(ctx, rv, "Cannot select object file"); + + rv = sc_erase_binary(p15card->card, 0, file->size, 0); + if (rv == SC_ERROR_SECURITY_STATUS_NOT_SATISFIED) { + rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_UPDATE); + LOG_TEST_RET(ctx, rv, "SC_AC_OP_UPDATE authentication failed"); + + rv = sc_erase_binary(p15card->card, 0, file->size, 0); + } + LOG_TEST_RET(ctx, rv, "Binary erase error"); + + sc_file_free(file); + } + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +/* + * Allocate a file + */ +static int +iasecc_pkcs15_new_file(struct sc_profile *profile, struct sc_card *card, + unsigned int type, unsigned int num, struct sc_file **out) +{ + struct sc_context *ctx = card->ctx; + struct sc_file *file = NULL; + const char *_template = NULL; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "type %X; num %i\n", type, num); + switch (type) { + case SC_PKCS15_TYPE_PRKEY_RSA: + _template = "private-key"; + break; + case SC_PKCS15_TYPE_PUBKEY_RSA: + _template = "public-key"; + break; + case SC_PKCS15_TYPE_CERT: + _template = "certificate"; + break; + case SC_PKCS15_TYPE_DATA_OBJECT: + _template = "public-data"; + break; + default: + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Profile template not supported"); + } + + sc_log(ctx, "df_info path '%s'\n", sc_print_path(&profile->df_info->file->path)); + rv = sc_profile_get_file(profile, _template, &file); + if (rv == SC_ERROR_FILE_NOT_FOUND) { + struct sc_pkcs15_id id; + + id.len = 1; + id.value[0] = num & 0xFF; + rv = sc_profile_instantiate_template(profile, "key-domain", &profile->df_info->file->path, + _template, &id, &file); + } + LOG_TEST_RET(ctx, rv, "Error when getting file from template"); + + sc_log(ctx, "path(type:%X;path:%s)\n", file->path.type, sc_print_path(&file->path)); + + file->id = (file->id & 0xFF00) | (num & 0xFF); + if (file->path.len == 0) { + file->path.type = SC_PATH_TYPE_FILE_ID; + file->path.len = 2; + } + file->path.value[file->path.len - 2] = (file->id >> 8) & 0xFF; + file->path.value[file->path.len - 1] = file->id & 0xFF; + file->path.count = -1; + + sc_log(ctx, "file size %i; ef type %i/%i; id %04X\n", file->size, file->type, file->ef_structure, file->id); + sc_log(ctx, "path type %X; path '%s'", file->path.type, sc_print_path(&file->path)); + + if (out) + *out = file; + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +/* + * Select a key reference + */ +static int +iasecc_pkcs15_select_key_reference(struct sc_profile *profile, struct sc_pkcs15_card *p15card, + struct sc_pkcs15_prkey_info *key_info) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_card *card = p15card->card; + struct sc_file *file = NULL; + int rv = 0, idx = key_info->key_reference & ~IASECC_OBJECT_REF_LOCAL; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "'seed' key reference %i; path %s", key_info->key_reference & ~IASECC_OBJECT_REF_LOCAL, + sc_print_path(&key_info->path)); + + rv = sc_select_file(card, &key_info->path, &file); + LOG_TEST_RET(ctx, rv, "Cannot select DF to select key reference in"); + + /* 1 <= ObjReference <= 31 */ + if (idx < IASECC_OBJECT_REF_MIN) + idx = IASECC_OBJECT_REF_MIN; + + /* Look for the suitable slot */ + if (idx <= IASECC_OBJECT_REF_MAX) { + struct iasecc_ctl_get_free_reference ctl_data; + + ctl_data.key_size = key_info->modulus_length; + ctl_data.usage = key_info->usage; + ctl_data.access = key_info->access_flags; + ctl_data.index = idx; + + rv = sc_card_ctl(card, SC_CARDCTL_IASECC_GET_FREE_KEY_REFERENCE, &ctl_data); + if (!rv) + sc_log(ctx, "found allocated slot %i", idx); + else if (rv == SC_ERROR_DATA_OBJECT_NOT_FOUND && idx <= IASECC_OBJECT_REF_MAX) + sc_log(ctx, "found empty slot %i", idx); + else + LOG_TEST_RET(ctx, rv, "Cannot select key reference"); + + idx = ctl_data.index; + } + + /* All card objects but PINs are locals */ + key_info->key_reference = idx | IASECC_OBJECT_REF_LOCAL; + sc_log(ctx, "selected key reference %i", key_info->key_reference); + + if (file) + sc_file_free(file); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_sdo_get_data(struct sc_card *card, struct iasecc_sdo *sdo) +{ + struct sc_context *ctx = card->ctx; + int rv; + + LOG_FUNC_CALLED(ctx); + rv = sc_card_ctl(card, SC_CARDCTL_IASECC_SDO_GET_DATA, sdo); + LOG_TEST_RET(ctx, rv, "IasEcc: GET DATA error"); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_file_convert_acls(struct sc_context *ctx, struct sc_profile *profile, struct sc_file *file) +{ +/* struct pin_info *pi = NULL, **tail = NULL; */ + int ii; + + for (ii=0; iimethod) { + case SC_AC_IDA: +#if 0 + for (tail = &profile->pin_list; (pi = *tail); tail = &pi->next) { + if (pi->pin.reference == acl->key_ref && pi->pin.sen_reference) { + acl->method = SC_AC_SEN; + acl->key_ref = pi->pin.sen_reference; + break; + } + if (pi->pin.reference == acl->key_ref && pi->pin.chv_reference) { + acl->method = SC_AC_CHV; + acl->key_ref = pi->pin.chv_reference; + break; + } + } + + if (!pi) { + sc_log(ctx, "convert ACLs error: no PIN found for SC_AC_IDA(ref:%X)", acl->key_ref); + LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_PROFILE, "Cannot convert ACL(s)"); + } + break; +#else + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "'IDA' not actually supported"); +#endif + case SC_AC_SCB: + if ((acl->key_ref & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_USER_AUTH) { + acl->method = SC_AC_SEN; + acl->key_ref &= IASECC_SCB_METHOD_MASK_REF; + } + else if ((acl->key_ref & IASECC_SCB_METHOD_MASK) == IASECC_SCB_METHOD_SM) { + acl->method = SC_AC_PRO; + acl->key_ref &= IASECC_SCB_METHOD_MASK_REF; + } + } + } + } + + return 0; +} + +static int +iasecc_sdo_set_key_acls_from_profile(struct sc_profile *profile, struct sc_card *card, + const char *template, struct iasecc_sdo *sdo) +{ + struct sc_context *ctx = card->ctx; + struct sc_file *file = NULL; + unsigned char ops_prvkey[7] = { + SC_AC_OP_PSO_COMPUTE_SIGNATURE, SC_AC_OP_INTERNAL_AUTHENTICATE, SC_AC_OP_PSO_DECRYPT, + SC_AC_OP_GENERATE, 0xFF, SC_AC_OP_UPDATE, SC_AC_OP_READ + }; + unsigned char ops_pubkey[7] = { + 0xFF, SC_AC_OP_EXTERNAL_AUTHENTICATE, 0xFF, + SC_AC_OP_GENERATE, 0xFF, SC_AC_OP_UPDATE, SC_AC_OP_READ + }; + unsigned char amb, scb[16], mask; + int rv, ii, cntr; + + LOG_FUNC_CALLED(ctx); + + /* Get ACLs from profile template */ + rv = sc_profile_get_file(profile, template, &file); + LOG_TEST_RET(ctx, rv, "IasEcc: cannot instanciate private key file"); + + /* Convert PKCS15 ACLs to SE ACLs */ + rv = iasecc_file_convert_acls(ctx, profile, file); + LOG_TEST_RET(ctx, rv, "Cannot convert profile ACLs"); + + memset(scb, 0, sizeof(scb)); + for (ii = 0, mask = 0x80, amb = 0x80, cntr = 0; ii < 7; ii++) { + const sc_acl_entry_t *acl; + unsigned char op = sdo->sdo_class == IASECC_SDO_CLASS_RSA_PRIVATE ? ops_prvkey[ii] : ops_pubkey[ii]; + + mask >>= 1; + + if (op == 0xFF) + continue; + + acl = sc_file_get_acl_entry(file, op); + sc_log(ctx, "ACL: 0x%X:0x%X", acl->method, acl->key_ref); + + if (acl->method == SC_AC_NEVER) { + } + else if (acl->method == SC_AC_NONE) { + amb |= mask; + scb[cntr++] = 0x00; + } + else if (acl->method == SC_AC_SEN || acl->method == SC_AC_PRO || acl->method == SC_AC_AUT) { + if ((acl->key_ref & 0xF) == 0 || (acl->key_ref & 0xF) == 0xF) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid SE reference"); + + amb |= mask; + + if (acl->method == SC_AC_SEN) + scb[cntr++] = acl->key_ref | IASECC_SCB_METHOD_USER_AUTH; + else if (acl->method == SC_AC_PRO) + scb[cntr++] = acl->key_ref | IASECC_SCB_METHOD_SM; + else + scb[cntr++] = acl->key_ref | IASECC_SCB_METHOD_EXT_AUTH; + } + else { + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Unknown SCB method"); + } + } + + /* Copy ACLs into the DOCP*/ + sdo->docp.acls_contact.tag = IASECC_DOCP_TAG_ACLS_CONTACT; + sdo->docp.acls_contact.size = cntr + 1; + sdo->docp.acls_contact.value = calloc(1, sdo->docp.acls_contact.size); + if (!sdo->docp.acls_contact.value) + return SC_ERROR_MEMORY_FAILURE; + *(sdo->docp.acls_contact.value + 0) = amb; + memcpy(sdo->docp.acls_contact.value + 1, scb, cntr); + + sc_log(ctx, "AMB: %X, CNTR %i, %x %x %x %x %x %x", + amb, cntr, scb[0], scb[1], scb[2], scb[3], scb[4], scb[5], scb[6]); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_sdo_allocate_prvkey(struct sc_profile *profile, struct sc_card *card, + struct sc_pkcs15_prkey_info *key_info, struct iasecc_sdo **out) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_sdo *sdo = NULL; + size_t sz = key_info->modulus_length / 8; + int rv; + + LOG_FUNC_CALLED(ctx); + + sdo = calloc(1, sizeof(struct iasecc_sdo)); + if (!sdo) + LOG_TEST_RET(ctx, SC_ERROR_MEMORY_FAILURE, "Cannot allocate 'iasecc_sdo'"); + + sdo->magic = SC_CARDCTL_IASECC_SDO_MAGIC; + sdo->sdo_ref = key_info->key_reference & 0x3F; + sdo->sdo_class = IASECC_SDO_CLASS_RSA_PRIVATE; + sdo->usage = key_info->usage; + + sc_log(ctx, "sdo->sdo_class 0x%X; sdo->usage 0x%X", sdo->sdo_class, sdo->usage); + + rv = iasecc_sdo_get_data(card, sdo); + if (rv == SC_ERROR_DATA_OBJECT_NOT_FOUND) { + sdo->not_on_card = 1; + + rv = iasecc_sdo_set_key_acls_from_profile(profile, card, "private-key", sdo); + LOG_TEST_RET(ctx, rv, "IasEcc: cannot set ACLs for SDO from the 'private-key'"); + + //sdo->docp.name = + //sdo->docp.idata = + + sdo->docp.non_repudiation.value = calloc(1, 1); + if (!sdo->docp.non_repudiation.value) + LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); + sdo->docp.non_repudiation.tag = IASECC_DOCP_TAG_NON_REPUDATION; + sdo->docp.non_repudiation.size = 1; + + sdo->data.prv_key.compulsory.value = calloc(1, 1); + if (!sdo->data.prv_key.compulsory.value) + LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); + sdo->data.prv_key.compulsory.tag = IASECC_SDO_PRVKEY_TAG_COMPULSORY; + sdo->data.prv_key.compulsory.size = 1; + + sdo->docp.size.value = calloc(1, 2); + if (!sdo->docp.size.value) + LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); + sdo->docp.size.tag = IASECC_DOCP_TAG_SIZE; + sdo->docp.size.size = 2; + *(sdo->docp.size.value + 0) = (sz >> 8) & 0xFF; + *(sdo->docp.size.value + 1) = sz & 0xFF; + + // TODO: Manage CRT key types: IASECC_GEN_KEY_TYPE_*: X509_usage + // Optional PRIVATE KEY SDO attribute 'Algorithm to compulsorily use' can have one of the three values: + // B6(Sign), A4(Authentication), B8(Confidentiality). + // If present, this attribute has to be the same in a 'GENERATE KEY' template data. + if (!(key_info->access_flags & SC_PKCS15_PRKEY_ACCESS_LOCAL) && (key_info->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)) + sc_log(ctx, "Non fatal error: NON_REPUDATION can be used only for the localy generated keys"); + + if ((key_info->access_flags & SC_PKCS15_PRKEY_ACCESS_LOCAL) + && (key_info->usage & SC_PKCS15_PRKEY_USAGE_SIGN) + && (key_info->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)) { + *(sdo->docp.non_repudiation.value + 0) = 1; + *(sdo->data.prv_key.compulsory.value + 0) = IASECC_CRT_TAG_DST; + } + + sc_log(ctx, "non_repudiation %i", *(sdo->docp.non_repudiation.value + 0)); + sc_log(ctx, "compulsory 0x%X", *(sdo->data.prv_key.compulsory.value + 0)); + } + else { + LOG_TEST_RET(ctx, rv, "IasEcc: error while getting private key SDO data"); + } + + if (out) + *out = sdo; + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_sdo_allocate_pubkey(struct sc_profile *profile, struct sc_card *card, struct sc_pkcs15_pubkey_info *key_info, + struct iasecc_sdo **out) +{ + struct sc_context *ctx = card->ctx; + struct iasecc_sdo *sdo = NULL; + size_t sz = key_info->modulus_length / 8; + int rv; + + LOG_FUNC_CALLED(ctx); + sdo = calloc(1, sizeof(struct iasecc_sdo)); + if (!sdo) + return SC_ERROR_MEMORY_FAILURE; + + sdo->magic = SC_CARDCTL_IASECC_SDO_MAGIC; + sdo->sdo_ref = key_info->key_reference & 0x3F; + sdo->sdo_class = IASECC_SDO_CLASS_RSA_PUBLIC; + + rv = iasecc_sdo_get_data(card, sdo); + sc_log(ctx, "get Public Key SDO(class:%X) data returned %i", sdo->sdo_class, rv); + if (rv == SC_ERROR_DATA_OBJECT_NOT_FOUND) { + sdo->not_on_card = 1; + + rv = iasecc_sdo_set_key_acls_from_profile(profile, card, "public-key", sdo); + LOG_TEST_RET(ctx, rv, "iasecc_sdo_allocate_pubkey() cannot set ACLs for SDO from the 'public-key'"); + + sdo->docp.size.value = calloc(1, 2); + if (!sdo->docp.size.value) + LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); + sdo->docp.size.size = 2; + sdo->docp.size.tag = IASECC_DOCP_TAG_SIZE; + *(sdo->docp.size.value + 0) = (sz >> 8) & 0xFF; + *(sdo->docp.size.value + 1) = sz & 0xFF; + + if (card->type == SC_CARD_TYPE_IASECC_OBERTHUR) { + printf("TODO: Disabled for the tests of the Oberthur card\n"); + } + else { + sdo->data.pub_key.cha.value = calloc(1, 2); + if (!sdo->data.pub_key.cha.value) + LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); + sdo->data.pub_key.cha.size = 2; + sdo->data.pub_key.cha.tag = IASECC_SDO_PUBKEY_TAG_CHA; + } + + sdo->data.pub_key.compulsory.value = calloc(1, 1); + if (!sdo->data.pub_key.compulsory.value) + LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); + sdo->data.pub_key.compulsory.tag = IASECC_SDO_PUBKEY_TAG_COMPULSORY; + sdo->data.pub_key.compulsory.size = 1; + } + else { + LOG_TEST_RET(ctx, rv, "iasecc_sdo_allocate_pubkey() error while getting public key SDO data"); + } + + if (out) + *out = sdo; + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_sdo_convert_to_file(struct sc_card *card, struct iasecc_sdo *sdo, struct sc_file **out) +{ + struct sc_context *ctx = card->ctx; + struct sc_file *file = sc_file_new(); + int rv, ii; + + LOG_FUNC_CALLED(ctx); + if (file == NULL) + LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); + else if (!card || !sdo) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + sc_log(card->ctx, "sdo->sdo_class %X", sdo->sdo_class); + + if (sdo->sdo_class == IASECC_SDO_CLASS_RSA_PRIVATE) { + unsigned char ops[] = { + SC_AC_OP_PSO_COMPUTE_SIGNATURE, SC_AC_OP_INTERNAL_AUTHENTICATE, SC_AC_OP_PSO_DECRYPT, + SC_AC_OP_GENERATE, SC_AC_OP_UPDATE, SC_AC_OP_READ + }; + + for (ii=0; iictx, "ii:%i, method:%X, ref:%X", ii, op_method, op_ref); + + sc_file_add_acl_entry(file, ops[ii], op_method, op_ref); + } + } + + if (out) + *out = file; + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_pkcs15_add_access_rule(struct sc_pkcs15_object *object, unsigned access_mode, struct sc_pkcs15_id *auth_id) +{ + int ii; + + for (ii=0;iiaccess_rules[ii].access_mode) { + object->access_rules[ii].access_mode = access_mode; + if (auth_id) + object->access_rules[ii].auth_id = *auth_id; + else + object->access_rules[ii].auth_id.len = 0; + break; + } + else if (!auth_id && !object->access_rules[ii].auth_id.len) { + object->access_rules[ii].access_mode |= access_mode; + break; + } + else if (auth_id && sc_pkcs15_compare_id(&object->access_rules[ii].auth_id, auth_id)) { + object->access_rules[ii].access_mode |= access_mode; + break; + } + } + + if (ii==SC_PKCS15_MAX_ACCESS_RULES) + return SC_ERROR_TOO_MANY_OBJECTS; + + return SC_SUCCESS; +} + + +static int +iasecc_pkcs15_get_auth_id_from_se(struct sc_pkcs15_card *p15card, unsigned char scb, + struct sc_pkcs15_id *auth_id) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_pkcs15_object *pin_objs[32]; + int rv, ii, nn_pins, se_ref, pin_ref; + + LOG_FUNC_CALLED(ctx); + if (!auth_id) + LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); + + memset(auth_id, 0, sizeof(struct sc_pkcs15_id)); + + if (!(scb & IASECC_SCB_METHOD_USER_AUTH)) + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + + rv = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, pin_objs, 32); + LOG_TEST_RET(ctx, rv, "Error while getting AUTH objects"); + nn_pins = rv; + + se_ref = scb & 0x0F; + rv = sc_card_ctl(p15card->card, SC_CARDCTL_GET_CHV_REFERENCE_IN_SE, (void *)(&se_ref)); + LOG_TEST_RET(ctx, rv, "Card CTL error: cannot get CHV reference from SE"); + pin_ref = rv; + for (ii=0; iidata; + + /* FIXME: make pin reference 'unsigned' */ + sc_log(ctx, "PIN refs %i/%i", pin_ref, pin_info->reference); + if (pin_ref == ((pin_info->reference + 0x100) % 0x100)) { + *auth_id = pin_info->auth_id; + break; + } + } + if (ii == nn_pins) + LOG_TEST_RET(ctx, SC_ERROR_OBJECT_NOT_FOUND, "No AUTH object found"); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_pkcs15_fix_file_access_rule(struct sc_pkcs15_card *p15card, struct sc_file *file, + unsigned ac_op, unsigned rule_mode, struct sc_pkcs15_object *object) +{ + struct sc_context *ctx = p15card->card->ctx; + const struct sc_acl_entry *acl = NULL; + struct sc_pkcs15_id id; + unsigned ref; + int rv; + + LOG_FUNC_CALLED(ctx); + acl = sc_file_get_acl_entry(file, ac_op); + sc_log(ctx, "Fix file access rule: AC_OP:%i, ACL(method:0x%X,ref:0x%X)", ac_op, acl->method, acl->key_ref); + if (acl->method == SC_AC_NONE) { + sc_log(ctx, "rule-mode:0x%X, auth-ID:NONE", rule_mode); + rv = iasecc_pkcs15_add_access_rule(object, rule_mode, NULL); + LOG_TEST_RET(ctx, rv, "Fix file access rule error"); + } + else { + if (acl->method == SC_AC_IDA) { + ref = acl->key_ref; + iasecc_reference_to_pkcs15_id (ref, &id); + } + else if (acl->method == SC_AC_SCB) { + rv = iasecc_pkcs15_get_auth_id_from_se(p15card, acl->key_ref, &id); + LOG_TEST_RET(ctx, rv, "Cannot get AUTH.ID from SE"); + } + else if (acl->method == SC_AC_PRO) { + ref = IASECC_SCB_METHOD_SM * 0x100 + acl->key_ref; + iasecc_reference_to_pkcs15_id (ref, &id); + } + else { + LOG_TEST_RET(ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Fix file access error"); + } + + sc_log(ctx, "rule-mode:0x%X, auth-ID:%s", rule_mode, sc_pkcs15_print_id(&id)); + rv = iasecc_pkcs15_add_access_rule(object, rule_mode, &id); + LOG_TEST_RET(ctx, rv, "Fix file access rule error"); + } + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_pkcs15_fix_file_access(struct sc_pkcs15_card *p15card, struct sc_file *file, + struct sc_pkcs15_object *object) +{ + struct sc_context *ctx = p15card->card->ctx; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "authID %s", sc_pkcs15_print_id(&object->auth_id)); + + memset(object->access_rules, 0, sizeof(object->access_rules)); + + rv = iasecc_pkcs15_fix_file_access_rule(p15card, file, SC_AC_OP_READ, SC_PKCS15_ACCESS_RULE_MODE_READ, object); + LOG_TEST_RET(ctx, rv, "Fix file READ access error"); + + rv = iasecc_pkcs15_fix_file_access_rule(p15card, file, SC_AC_OP_UPDATE, SC_PKCS15_ACCESS_RULE_MODE_UPDATE, object); + LOG_TEST_RET(ctx, rv, "Fix file READ access error"); + + rv = iasecc_pkcs15_fix_file_access_rule(p15card, file, SC_AC_OP_DELETE, SC_PKCS15_ACCESS_RULE_MODE_DELETE, object); + LOG_TEST_RET(ctx, rv, "Fix file READ access error"); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_pkcs15_encode_supported_algos(struct sc_pkcs15_card *p15card, struct sc_pkcs15_object *object) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *) object->data; + struct sc_supported_algo_info *algo; + int rv = SC_SUCCESS, ii; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "encode supported algos for object(%s,type:%X)", object->label, object->type); + switch (object->type) { + case SC_PKCS15_TYPE_PRKEY_RSA: + sc_log(ctx, "PrKey Usage:%X,Access:%X", prkey_info->usage, prkey_info->access_flags); + if (prkey_info->usage & (SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP)) { + algo = sc_pkcs15_get_supported_algo(p15card, SC_PKCS15_ALGO_OP_DECIPHER, CKM_RSA_PKCS); + rv = sc_pkcs15_add_supported_algo_ref(object, algo); + LOG_TEST_RET(ctx, rv, "cannot add supported algorithm DECIPHER:CKM_RSA_PKCS"); + } + + if (prkey_info->usage & SC_PKCS15_PRKEY_USAGE_SIGN) { + if (prkey_info->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION) { + algo = sc_pkcs15_get_supported_algo(p15card, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_SHA1_RSA_PKCS); + rv = sc_pkcs15_add_supported_algo_ref(object, algo); + LOG_TEST_RET(ctx, rv, "cannot add supported algorithm SIGN:CKM_SHA1_RSA_PKCS"); + + algo = sc_pkcs15_get_supported_algo(p15card, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_SHA256_RSA_PKCS); + rv = sc_pkcs15_add_supported_algo_ref(object, algo); + LOG_TEST_RET(ctx, rv, "cannot add supported algorithm SIGN:CKM_SHA256_RSA_PKCS"); + } + else { + algo = sc_pkcs15_get_supported_algo(p15card, SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE, CKM_RSA_PKCS); + rv = sc_pkcs15_add_supported_algo_ref(object, algo); + LOG_TEST_RET(ctx, rv, "cannot add supported algorithm SIGN:CKM_RSA_PKCS"); + } + } + + for (ii=0; iialgo_refs[ii]; ii++) + sc_log(ctx, "algoReference %i", prkey_info->algo_refs[ii]); + break; + default: + rv = SC_ERROR_NOT_SUPPORTED; + break; + } + + LOG_FUNC_RETURN(ctx, rv); +} + + +/* + * Store SDO key RSA + */ +static int +iasecc_sdo_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, + struct iasecc_sdo *sdo_prvkey, struct iasecc_sdo *sdo_pubkey, + struct sc_pkcs15_prkey_rsa *rsa) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_card *card = p15card->card; + unsigned long caps = card->caps; + struct iasecc_sdo_rsa_update update; + struct sc_file *dummy_file = NULL; + int rv; + + LOG_FUNC_CALLED(ctx); + + if (!sdo_prvkey && !sdo_pubkey) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "At least one SDO has to be supplied"); + rv = iasecc_sdo_convert_to_file(card, sdo_prvkey ? sdo_prvkey : sdo_pubkey, &dummy_file); + LOG_TEST_RET(ctx, rv, "Cannot convert SDO PRIVATE KEY to file"); + + card->caps &= ~SC_CARD_CAP_USE_FCI_AC; + rv = sc_pkcs15init_authenticate(profile, p15card, dummy_file, SC_AC_OP_UPDATE); + card->caps = caps; + LOG_TEST_RET(ctx, rv, "SDO PRIVATE KEY UPDATE authentication failed"); + + if (dummy_file) + sc_file_free(dummy_file); + + memset(&update, 0, sizeof(update)); + + update.sdo_prv_key = sdo_prvkey; + update.sdo_pub_key = sdo_pubkey; + update.p15_rsa = rsa; + update.magic = IASECC_SDO_MAGIC_UPDATE_RSA; + + rv = sc_card_ctl(card, SC_CARDCTL_IASECC_SDO_KEY_RSA_PUT_DATA, &update); + LOG_TEST_RET(ctx, rv, "store IAS SDO PRIVATE KEY failed"); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_pkcs15_add_algorithm_reference(struct sc_pkcs15_card *p15card, + struct sc_pkcs15_prkey_info *key_info, unsigned algo_ref) +{ + int ii, jj; + + for (jj=0;jjalgo_refs[jj];jj++) + ; + if (jj == SC_MAX_SUPPORTED_ALGORITHMS) + return SC_ERROR_TOO_MANY_OBJECTS; + + for (ii=0;iitokeninfo->supported_algos[ii].algo_ref == algo_ref) + break; + if (ii == SC_MAX_SUPPORTED_ALGORITHMS) + return SC_ERROR_OBJECT_NOT_FOUND; + + key_info->algo_refs[jj] = p15card->tokeninfo->supported_algos[ii].reference; + return SC_SUCCESS; +} + + +static int +iasecc_pkcs15_fix_private_key_attributes(struct sc_profile *profile, struct sc_pkcs15_card *p15card, + struct sc_pkcs15_object *object, + struct iasecc_sdo *sdo_prvkey) +{ + struct sc_card *card = p15card->card; + struct sc_context *ctx = card->ctx; + struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; + int rv = 0, ii; + unsigned keys_access_modes[IASECC_MAX_SCBS] = { + SC_PKCS15_ACCESS_RULE_MODE_PSO_CDS, SC_PKCS15_ACCESS_RULE_MODE_INT_AUTH, SC_PKCS15_ACCESS_RULE_MODE_PSO_DECRYPT, + SC_PKCS15_ACCESS_RULE_MODE_EXECUTE, 0x00, SC_PKCS15_ACCESS_RULE_MODE_UPDATE, SC_PKCS15_ACCESS_RULE_MODE_READ + }; + + LOG_FUNC_CALLED(ctx); + if (!object->content.value || object->content.len != sizeof(struct iasecc_sdo)) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "store IAS SDO PRIVATE KEY failed"); + + if (object->type != SC_PKCS15_TYPE_PRKEY_RSA) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unsupported object type"); + + key_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_SENSITIVE; + key_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE; + key_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE; + + sc_log(ctx, "SDO(class:%X,ref:%X,usage:%X)", + sdo_prvkey->sdo_class, sdo_prvkey->sdo_ref, sdo_prvkey->usage); + sc_log(ctx, "SDO ACLs(%i):%s", sdo_prvkey->docp.acls_contact.size, + sc_dump_hex(sdo_prvkey->docp.acls_contact.value, sdo_prvkey->docp.acls_contact.size)); + sc_log(ctx, "SDO AMB:%X, SCBS:%s", sdo_prvkey->docp.amb, + sc_dump_hex(sdo_prvkey->docp.scbs, IASECC_MAX_SCBS)); + + for (ii=0;iidocp.scbs[ii]); + if (sdo_prvkey->docp.scbs[ii] == 0xFF) { + continue; + } + else if (sdo_prvkey->docp.scbs[ii] == 0x00) { + rv = iasecc_pkcs15_add_access_rule(object, keys_access_modes[ii], NULL); + LOG_TEST_RET(ctx, rv, "Cannot add access rule"); + } + else if (sdo_prvkey->docp.scbs[ii] & IASECC_SCB_METHOD_USER_AUTH) { + struct sc_pkcs15_id auth_id; + + rv = iasecc_pkcs15_get_auth_id_from_se(p15card, sdo_prvkey->docp.scbs[ii], &auth_id); + LOG_TEST_RET(ctx, rv, "Cannot get AUTH.ID from SE"); + + rv = iasecc_pkcs15_add_access_rule(object, keys_access_modes[ii], &auth_id); + LOG_TEST_RET(ctx, rv, "Cannot add access rule"); + + if (ii == IASECC_ACLS_RSAKEY_PSO_SIGN + || ii == IASECC_ACLS_RSAKEY_INTERNAL_AUTH + || ii == IASECC_ACLS_RSAKEY_PSO_DECIPHER) { + if (!sc_pkcs15_compare_id(&object->auth_id, &auth_id)) { + /* Sorry, this will silently overwrite the profile option.*/ + sc_log(ctx, "Change object's authId for the one that really protects crypto operation."); + object->auth_id = auth_id; + } + } + } + + if (ii == IASECC_ACLS_RSAKEY_PSO_SIGN) { + rv = iasecc_pkcs15_add_algorithm_reference(p15card, key_info, + IASECC_ALGORITHM_RSA_PKCS | IASECC_ALGORITHM_SHA1); + LOG_TEST_RET(ctx, rv, "Cannot add RSA_PKCS SHA1 supported mechanism"); + + rv = iasecc_pkcs15_add_algorithm_reference(p15card, key_info, + IASECC_ALGORITHM_RSA_PKCS | IASECC_ALGORITHM_SHA2); + LOG_TEST_RET(ctx, rv, "Cannot add RSA_PKCS SHA2 supported mechanism"); + + key_info->usage |= SC_PKCS15_PRKEY_USAGE_SIGN; + if (sdo_prvkey->docp.non_repudiation.value && sdo_prvkey->docp.non_repudiation.value[0]) + key_info->usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; + } + else if (ii == IASECC_ACLS_RSAKEY_INTERNAL_AUTH) { + rv = iasecc_pkcs15_add_algorithm_reference(p15card, key_info, IASECC_ALGORITHM_RSA_PKCS); + LOG_TEST_RET(ctx, rv, "Cannot add RSA_PKCS supported mechanism"); + + key_info->usage |= SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER; + } + else if (ii == IASECC_ACLS_RSAKEY_PSO_DECIPHER) { + rv = iasecc_pkcs15_add_algorithm_reference(p15card, key_info, + IASECC_ALGORITHM_RSA_PKCS_DECRYPT | IASECC_ALGORITHM_SHA1); + LOG_TEST_RET(ctx, rv, "Cannot add decipher RSA_PKCS supported mechanism"); + + key_info->usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP; + } + } + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_pkcs15_create_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, + struct sc_pkcs15_object *object) +{ + struct sc_card *card = p15card->card; + struct sc_context *ctx = card->ctx; + struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; + struct iasecc_sdo *sdo_prvkey = NULL, *sdo_pubkey = NULL; + size_t keybits = key_info->modulus_length; + unsigned char zeros[0x200]; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "create private key(keybits:%i,usage:%X,access:%X,ref:%X)", + keybits, key_info->usage, key_info->access_flags, key_info->key_reference); + if (keybits < 1024 || keybits > 2048 || (keybits % 256)) { + sc_log(ctx, "Unsupported key size %u", keybits); + return SC_ERROR_INVALID_ARGUMENTS; + } + + memset(zeros, 0, sizeof(zeros)); + + rv = iasecc_sdo_allocate_pubkey(profile, card, (struct sc_pkcs15_pubkey_info *)key_info, &sdo_pubkey); + LOG_TEST_RET(ctx, rv, "IasEcc: allocate SDO public key failed"); + sc_log(ctx, "iasecc_pkcs15_create_key() sdo_pubkey->not_on_card %i", sdo_pubkey->not_on_card); + + rv = iasecc_sdo_allocate_prvkey(profile, card, key_info, &sdo_prvkey); + LOG_TEST_RET(ctx, rv, "IasEcc: init SDO private key failed"); + sc_log(ctx, "iasecc_pkcs15_create_key() sdo_prvkey->not_on_card %i", sdo_prvkey->not_on_card); + + if (!sdo_prvkey->not_on_card && !sdo_pubkey->not_on_card) + sc_log(ctx, "Key ref %i already allocated", key_info->key_reference); + else + LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "SDO creation is not supported for the IAS/ECC cards"); + + rv = sc_pkcs15_allocate_object_content(object, (unsigned char *)sdo_prvkey, sizeof(struct iasecc_sdo)); + LOG_TEST_RET(ctx, rv, "Failed to allocate PrvKey SDO as object content"); + + rv = iasecc_pkcs15_fix_private_key_attributes(profile, p15card, object, (struct iasecc_sdo *)object->content.value); + LOG_TEST_RET(ctx, rv, "Failed to fix private key PKCS#15 attributes"); + + key_info->path.len = 0; + + iasecc_sdo_free(card, sdo_pubkey); + + LOG_FUNC_RETURN(ctx, rv); +} + + +/* + * RSA key generation + */ +static int +iasecc_pkcs15_generate_key(struct sc_profile *profile, sc_pkcs15_card_t *p15card, + struct sc_pkcs15_object *object, struct sc_pkcs15_pubkey *pubkey) +{ + struct sc_card *card = p15card->card; + struct sc_context *ctx = card->ctx; + struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; + size_t keybits = key_info->modulus_length; + struct iasecc_sdo *sdo_prvkey = NULL; + struct iasecc_sdo *sdo_pubkey = NULL; + struct sc_file *file = NULL; + unsigned long caps; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "generate key(bits:%i,path:%s,AuthID:%s\n", keybits, + sc_print_path(&key_info->path), sc_pkcs15_print_id(&object->auth_id)); + + if (!object->content.value || object->content.len != sizeof(struct iasecc_sdo)) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid PrKey SDO data"); + + sdo_prvkey = (struct iasecc_sdo *)object->content.value; + if (sdo_prvkey->magic != SC_CARDCTL_IASECC_SDO_MAGIC) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "'Magic' control failed for SDO PrvKey"); + + if (keybits < 1024 || keybits > 2048 || (keybits%0x100)) { + sc_log(ctx, "Unsupported key size %u\n", keybits); + return SC_ERROR_INVALID_ARGUMENTS; + } + + printf("TODO: Check if native IAS middleware accepts the meaningfull path value.\n"); + + rv = sc_profile_get_parent(profile, "private-key", &file); + LOG_TEST_RET(ctx, rv, "IasEcc: cannot get private key parent file"); + + rv = sc_select_file(card, &file->path, NULL); + LOG_TEST_RET(ctx, rv, "DF for private objects not defined"); + + if (file) + sc_file_free(file); + + rv = iasecc_sdo_convert_to_file(card, sdo_prvkey, &file); + LOG_TEST_RET(ctx, rv, "Cannot convert SDO PRIVKEY to file"); + + caps = card->caps; + card->caps &= ~SC_CARD_CAP_USE_FCI_AC; + rv = sc_pkcs15init_authenticate(profile, p15card, file, SC_AC_OP_GENERATE); + card->caps = caps; + LOG_TEST_RET(ctx, rv, "SC_AC_OP_GENERATE authentication failed"); + + key_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_LOCAL; + + rv = sc_card_ctl(card, SC_CARDCTL_IASECC_SDO_GENERATE, sdo_prvkey); + LOG_TEST_RET(ctx, rv, "generate key failed"); + + /* Quite dangerous -- cast of 'sc_pkcs15_prvkey_info' into 'sc_pkcs15_pubkey_info'. */ + rv = iasecc_sdo_allocate_pubkey(profile, card, (struct sc_pkcs15_pubkey_info *)key_info, &sdo_pubkey); + LOG_TEST_RET(ctx, rv, "IasEcc: allocate SDO public key failed"); + + pubkey->algorithm = SC_ALGORITHM_RSA; + + pubkey->u.rsa.modulus.len = sdo_pubkey->data.pub_key.n.size; + pubkey->u.rsa.modulus.data = (unsigned char *) malloc(pubkey->u.rsa.modulus.len); + if (!pubkey->u.rsa.modulus.data) + LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); + memcpy(pubkey->u.rsa.modulus.data, sdo_pubkey->data.pub_key.n.value, pubkey->u.rsa.modulus.len); + + pubkey->u.rsa.exponent.len = sdo_pubkey->data.pub_key.e.size; + pubkey->u.rsa.exponent.data = (unsigned char *) malloc(pubkey->u.rsa.exponent.len); + if (!pubkey->u.rsa.exponent.data) + LOG_FUNC_RETURN(ctx, SC_ERROR_MEMORY_FAILURE); + memcpy(pubkey->u.rsa.exponent.data, sdo_pubkey->data.pub_key.e.value, pubkey->u.rsa.exponent.len); + + rv = sc_pkcs15_encode_pubkey(ctx, pubkey, &pubkey->data.value, &pubkey->data.len); + LOG_TEST_RET(ctx, rv, "encode public key failed"); + + rv = iasecc_pkcs15_encode_supported_algos(p15card, object); + LOG_TEST_RET(ctx, rv, "encode private key access rules failed"); + + /* SDO PrvKey data replaced by public part of generated key */ + rv = sc_pkcs15_allocate_object_content(object, pubkey->data.value, pubkey->data.len); + LOG_TEST_RET(ctx, rv, "Failed to allocate public key as object content"); + + iasecc_sdo_free(card, sdo_pubkey); + + LOG_FUNC_RETURN(ctx, rv); +} + + +/* + * Store a private key + */ +static int +iasecc_pkcs15_store_key(struct sc_profile *profile, struct sc_pkcs15_card *p15card, + struct sc_pkcs15_object *object, struct sc_pkcs15_prkey *prvkey) +{ + struct sc_card *card = p15card->card; + struct sc_context *ctx = card->ctx; + struct sc_pkcs15_prkey_info *key_info = (struct sc_pkcs15_prkey_info *) object->data; + size_t keybits = key_info->modulus_length; + struct iasecc_sdo *sdo_prvkey; + struct iasecc_sdo *sdo_pubkey = NULL; + struct sc_pkcs15_prkey_rsa *rsa = &prvkey->u.rsa; + struct sc_file *file = NULL; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "Store IAS/ECC key(keybits:%i,AuthID:%s,path:%s)", + keybits, sc_pkcs15_print_id(&object->auth_id), sc_print_path(&key_info->path)); + + if (!object->content.value || object->content.len != sizeof(struct iasecc_sdo)) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "Invalid PrKey SDO data"); + else if (keybits < 1024 || keybits > 2048 || (keybits%0x100)) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Unsupported key size"); + + sdo_prvkey = (struct iasecc_sdo *)object->content.value; + if (sdo_prvkey->magic != SC_CARDCTL_IASECC_SDO_MAGIC) + LOG_TEST_RET(ctx, SC_ERROR_INVALID_DATA, "'Magic' control failed for SDO PrvKey"); + + sc_log(ctx, "key compulsory attr(size:%i,on_card:%i)", + sdo_prvkey->data.prv_key.compulsory.size, sdo_prvkey->data.prv_key.compulsory.on_card); + + rv = sc_profile_get_parent(profile, "private-key", &file); + LOG_TEST_RET(ctx, rv, "cannot instantiate parent DF of the private key"); + + rv = sc_select_file(card, &file->path, NULL); + LOG_TEST_RET(ctx, rv, "failed to select parent DF"); + + if (file) + sc_file_free(file); + + key_info->access_flags &= ~SC_PKCS15_PRKEY_ACCESS_LOCAL; + + rv = iasecc_sdo_allocate_pubkey(profile, card, (struct sc_pkcs15_pubkey_info *)key_info, &sdo_pubkey); + LOG_TEST_RET(ctx, rv, "private key store failed: cannot allocate 'SDO PUBLIC KEY'"); + + rv = iasecc_sdo_store_key(profile, p15card, sdo_prvkey, sdo_pubkey, rsa); + LOG_TEST_RET(ctx, rv, "cannot store SDO PRIVATE/PUBLIC KEYs"); + + /* sdo_prvkey is freed while object is freeing */ + iasecc_sdo_free(card, sdo_pubkey); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_pkcs15_delete_sdo (struct sc_profile *profile, struct sc_pkcs15_card *p15card, + int sdo_class, int ref) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_card *card = p15card->card; + struct iasecc_sdo *sdo = NULL; + struct sc_pkcs15_prkey_rsa rsa; + struct sc_file *dummy_file = NULL; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "iasecc_pkcs15_delete_sdo() class 0x%X; reference %i", sdo_class, ref); + + sdo = calloc(1, sizeof(struct iasecc_sdo)); + if (!sdo) + return SC_ERROR_MEMORY_FAILURE; + + sdo->magic = SC_CARDCTL_IASECC_SDO_MAGIC; + sdo->sdo_class = sdo_class; + sdo->sdo_ref = ref & 0x3F; + + rv = iasecc_sdo_get_data(card, sdo); + if (rv == SC_ERROR_OBJECT_NOT_FOUND) { + rv = 0; + } + else if (!rv) { + unsigned char zeros[0x200]; + int size = *(sdo->docp.size.value + 0) * 0x100 + *(sdo->docp.size.value + 1); + unsigned long caps = card->caps; + + sc_log(ctx, "iasecc_pkcs15_delete_sdo() SDO class %X, ref %i, size %i bytes", sdo->sdo_class, sdo->sdo_ref, size); + rv = iasecc_sdo_convert_to_file(card, sdo, &dummy_file); + LOG_TEST_RET(ctx, rv, "iasecc_pkcs15_delete_sdo() Cannot convert SDO to file"); + + card->caps &= ~SC_CARD_CAP_USE_FCI_AC; + rv = sc_pkcs15init_authenticate(profile, p15card, dummy_file, SC_AC_OP_UPDATE); + card->caps = caps; + LOG_TEST_RET(ctx, rv, "iasecc_pkcs15_delete_sdo() UPDATE authentication failed for SDO"); + + if (dummy_file) + sc_file_free(dummy_file); + + memset(zeros, 0, sizeof(zeros)); + memset(&rsa, 0, sizeof(rsa)); + + rsa.modulus.data = rsa.exponent.data = zeros; + rsa.modulus.len = size; + rsa.exponent.len = 3; + + rsa.p.data = rsa.q.data = rsa.iqmp.data = rsa.dmp1.data = rsa.dmq1.data = zeros; + rsa.p.len = rsa.q.len = rsa.iqmp.len = rsa.dmp1.len = rsa.dmq1.len = size/2; + + /* Don't know why, but, clean public key do not working with Gemalto card */ + rv = iasecc_sdo_store_key(profile, p15card, sdo, NULL, &rsa); + LOG_TEST_RET(ctx, rv, "iasecc_pkcs15_delete_sdo() store empty private key failed"); + } + else { + LOG_TEST_RET(ctx, rv, "iasecc_pkcs15_delete_sdo() cannot get data of SDO to be deleted"); + } + + iasecc_sdo_free(card, sdo); + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_pkcs15_delete_object (struct sc_profile *profile, struct sc_pkcs15_card *p15card, + unsigned int type, const void *data, const struct sc_path *path) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_file *file = sc_file_new(); + int rv, key_ref; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "delete PKCS15 object: type %X; path %s\n", type, sc_print_path(path)); + + type &= SC_PKCS15_TYPE_CLASS_MASK; + switch(type) { + case SC_PKCS15_TYPE_PUBKEY: + key_ref = ((sc_pkcs15_pubkey_info_t *)data)->key_reference; + sc_log(ctx, "Ignore delete of the SDO-PUBLIC-KEY(ref:%X)", key_ref); + LOG_FUNC_RETURN(ctx, SC_SUCCESS); + case SC_PKCS15_TYPE_PRKEY: + key_ref = ((sc_pkcs15_prkey_info_t *)data)->key_reference; + rv = iasecc_pkcs15_delete_sdo (profile, p15card, IASECC_SDO_CLASS_RSA_PRIVATE, key_ref); + sc_log(ctx, "delete SDO PRIVATE KEY with ref %X returns %i\n", key_ref, rv); + LOG_FUNC_RETURN(ctx, rv); + case SC_PKCS15_TYPE_CERT: + break; + case SC_PKCS15_TYPE_DATA_OBJECT: + break; + default: + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED); + } + + file->type = SC_FILE_TYPE_WORKING_EF; + file->ef_structure = SC_FILE_EF_TRANSPARENT; + file->id = path->value[path->len-2] * 0x100 + path->value[path->len-1]; + memcpy(&file->path, path, sizeof(file->path)); + + rv = iasecc_pkcs15_delete_file(p15card, profile, file); + + sc_file_free(file); + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_store_pubkey(struct sc_pkcs15_card *p15card, struct sc_profile *profile, struct sc_pkcs15_object *object, + struct sc_pkcs15_der *data, struct sc_path *path) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_pkcs15_pubkey_info *pubkey_info = (struct sc_pkcs15_pubkey_info *)object->data; + struct sc_pkcs15_prkey_info *prkey_info = NULL; + struct sc_pkcs15_object *prkey_object = NULL; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "Public Key id '%s'", sc_pkcs15_print_id(&pubkey_info->id)); + + rv = sc_pkcs15_find_prkey_by_id(p15card, &pubkey_info->id, &prkey_object); + LOG_TEST_RET(ctx, rv, "Find related PrKey error"); + + prkey_info = (struct sc_pkcs15_prkey_info *)prkey_object->data; + + pubkey_info->key_reference = prkey_info->key_reference; + + pubkey_info->access_flags = prkey_info->access_flags & SC_PKCS15_PRKEY_ACCESS_LOCAL; + pubkey_info->access_flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE; + + pubkey_info->native = 0; + + pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_SIGN ? SC_PKCS15_PRKEY_USAGE_VERIFY : 0; + pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_SIGNRECOVER ? SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER : 0; + pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_NONREPUDIATION ? SC_PKCS15_PRKEY_USAGE_VERIFY : 0; + pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_DECRYPT ? SC_PKCS15_PRKEY_USAGE_ENCRYPT : 0; + pubkey_info->usage |= prkey_info->usage & SC_PKCS15_PRKEY_USAGE_UNWRAP ? SC_PKCS15_PRKEY_USAGE_WRAP : 0; + + iasecc_pkcs15_add_access_rule(object, SC_PKCS15_ACCESS_RULE_MODE_READ, NULL); + + memcpy(&pubkey_info->algo_refs[0], &prkey_info->algo_refs[0], sizeof(pubkey_info->algo_refs)); + + LOG_FUNC_RETURN(ctx, SC_SUCCESS); +} + + +static int +iasecc_store_cert(struct sc_pkcs15_card *p15card, struct sc_profile *profile, + struct sc_pkcs15_object *object, struct sc_pkcs15_der *data, + struct sc_path *path) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_card *card = p15card->card; + struct sc_file *pfile = NULL; + int rv; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "iasecc_store_cert() authID '%s'", sc_pkcs15_print_id(&object->auth_id)); + + rv = iasecc_pkcs15_new_file(profile, card, SC_PKCS15_TYPE_CERT, 0, &pfile); + LOG_TEST_RET(ctx, rv, "IasEcc new CERT file error"); + + rv = iasecc_pkcs15_fix_file_access(p15card, pfile, object); + LOG_TEST_RET(ctx, rv, "encode file access rules failed"); + + /* NOT_IMPLEMENTED error code indicates to the upper call to execute the default 'store data' procedure */ + LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_IMPLEMENTED); +} + + +#if 0 +static int +iasecc_store_opaqueDO(struct sc_pkcs15_card *p15card, struct sc_profile *profile, + struct sc_pkcs15_object *object, struct sc_pkcs15_id *id, + struct sc_pkcs15_der *data, struct sc_path *path) +{ + struct sc_context *ctx = p15card->card->ctx; + struct sc_card *card = p15card->card; + struct sc_pkcs15_object *p15objects[0x40]; + struct sc_file *cfile = NULL, *pfile = NULL, *parent = NULL; + int rv, nn_objs, indx, ii; + + LOG_FUNC_CALLED(ctx); + sc_log(ctx, "iasecc_store_opaqueDO() id '%s'", sc_pkcs15_print_id(id)); + sc_log(ctx, "iasecc_store_opaqueDO() authID '%s'", sc_pkcs15_print_id(&object->auth_id)); + nn_objs = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_DATA_OBJECT, p15objects, 0x40); + LOG_TEST_RET(ctx, nn_objs, "IasEcc get pkcs15 DATA objects error"); + + for(indx = 1; indx < 0x40; indx++) { + struct sc_path fpath; + + rv = iasecc_pkcs15_new_file(profile, card, SC_PKCS15_TYPE_DATA_OBJECT, indx, &pfile); + LOG_TEST_RET(ctx, rv, "iasecc_store_opaqueDO() pkcs15 new DATA file error"); + + fpath = pfile->path; + + for (ii=0; iidata; + int file_id = info->path.value[info->path.len - 2] * 0x100 + info->path.value[info->path.len - 1]; + + sc_log(ctx, "iasecc_store_opaqueDO() %i: file_id 0x%X, pfile->id 0x%X\n", ii, file_id, pfile->id); + if (pfile->id == file_id) + break; + } + + if (ii == nn_objs) + break; + + if (pfile) + sc_file_free(pfile); + pfile = NULL; + } + + if (indx == 0x40) + LOG_TEST_RET(ctx, SC_ERROR_TOO_MANY_OBJECTS, "iasecc_store_opaqueDO() too many DATA objects."); + + do { + const struct sc_acl_entry *acl; + + memset(object->access_rules, 0, sizeof(object->access_rules)); + + object->access_rules[0].access_mode = SC_PKCS15_ACCESS_RULE_MODE_READ; + acl = sc_file_get_acl_entry(pfile, SC_AC_OP_READ); + sc_log(ctx, "iasecc_store_opaqueDO() READ method %i", acl->method); + if (acl->method == SC_AC_IDA) + iasecc_reference_to_pkcs15_id (acl->key_ref, &object->access_rules[0].auth_id); + + object->access_rules[1].access_mode = SC_PKCS15_ACCESS_RULE_MODE_UPDATE; + acl = sc_file_get_acl_entry(pfile, SC_AC_OP_UPDATE); + sc_log(ctx, "iasecc_store_opaqueDO() UPDATE method %i", acl->method); + if (acl->method == SC_AC_IDA) + iasecc_reference_to_pkcs15_id (acl->key_ref, &object->access_rules[1].auth_id); + + } while(0); + + rv = iasecc_file_convert_acls(ctx, profile, pfile); + LOG_TEST_RET(ctx, rv, "iasecc_store_opaqueDO() cannot convert profile ACLs"); + + sc_log(ctx, "sc_pkcs15init_store_opaqueDO() indx %i; pfile->parent.len %i\n", indx, pfile->parent_path.len); + sc_log(ctx, "sc_pkcs15init_store_opaqueDO() profile parent path '%s'\n", sc_print_path(&pfile->parent_path)); + + rv = sc_select_file(card, &pfile->parent_path, &parent); + LOG_TEST_RET(ctx, rv, "iasecc_store_opaqueDO() cannot select DATA's parent"); + sc_log(ctx, "iasecc_store_opaqueDO() parent path '%s'\n", sc_print_path(&parent->path)); + + sc_ctx_suppress_errors_on(ctx); + rv = sc_select_file(card, &pfile->path, &cfile); + sc_ctx_suppress_errors_off(ctx); + if (!rv) { + rv = sc_pkcs15init_authenticate(profile, p15card, cfile, SC_AC_OP_DELETE); + LOG_TEST_RET(ctx, rv, "iasecc_store_opaqueDO() DELETE authentication failed"); + + rv = iasecc_pkcs15_delete_file(p15card, profile, cfile); + LOG_TEST_RET(ctx, rv, "s_pkcs15init_store_opaqueDO() delete pkcs15 file error"); + } + else if (rv != SC_ERROR_FILE_NOT_FOUND) { + LOG_TEST_RET(ctx, rv, "iasecc_store_opaqueDO() select file error"); + } + + rv = sc_pkcs15init_authenticate(profile, p15card, parent, SC_AC_OP_CREATE); + LOG_TEST_RET(ctx, rv, "iasecc_store_opaqueDO() parent CREATE authentication failed"); + + pfile->size = data->len; + rv = sc_create_file(card, pfile); + LOG_TEST_RET(ctx, rv, "iasecc_store_opaqueDO() cannot create DATA file"); + + rv = sc_pkcs15init_authenticate(profile, p15card, pfile, SC_AC_OP_UPDATE); + LOG_TEST_RET(ctx, rv, "iasecc_store_opaqueDO() data file UPDATE authentication failed"); + + rv = sc_update_binary(card, 0, data->value, data->len, 0); + LOG_TEST_RET(ctx, rv, "iasecc_store_opaqueDO() update DATA file failed"); + + if (path) + *path = pfile->path; + + if (parent) + sc_file_free(parent); + + if (pfile) + sc_file_free(pfile); + + if (cfile) + sc_file_free(cfile); + + LOG_FUNC_RETURN(ctx, rv); +} +#endif + + +static int +iasecc_emu_store_data(struct sc_pkcs15_card *p15card, struct sc_profile *profile, + struct sc_pkcs15_object *object, + struct sc_pkcs15_der *data, struct sc_path *path) + +{ + struct sc_context *ctx = p15card->card->ctx; + int rv = SC_ERROR_NOT_IMPLEMENTED; + + LOG_FUNC_CALLED(ctx); + + switch (object->type & SC_PKCS15_TYPE_CLASS_MASK) { + case SC_PKCS15_TYPE_PUBKEY: + rv = iasecc_store_pubkey(p15card, profile, object, data, path); + break; + case SC_PKCS15_TYPE_CERT: + rv = iasecc_store_cert(p15card, profile, object, data, path); + break; +#if 0 + case SC_PKCS15_TYPE_DATA_OBJECT: + rv = iasecc_store_opaqueDO(p15card, profile, object, id, data, path); + break; +#endif + } + + LOG_FUNC_RETURN(ctx, rv); +} + + +static int +iasecc_emu_update_tokeninfo(struct sc_profile *profile, struct sc_pkcs15_card *p15card, + struct sc_pkcs15_tokeninfo *tinfo) +{ + LOG_FUNC_RETURN(p15card->card->ctx, SC_SUCCESS); +} + + +static struct sc_pkcs15init_operations +sc_pkcs15init_iasecc_operations = { + iasecc_pkcs15_erase_card, + NULL, /* init_card */ + NULL, /* create_dir */ + NULL, /* create_domain */ + NULL, /* select_pin_reference */ + NULL, /* create_pin */ + iasecc_pkcs15_select_key_reference, + iasecc_pkcs15_create_key, + iasecc_pkcs15_store_key, + iasecc_pkcs15_generate_key, + NULL, /* encode private key */ + NULL, /* encode public key */ + NULL, /* finalize_card */ + iasecc_pkcs15_delete_object, + + /* pkcs15init emulation */ + NULL, + NULL, + iasecc_emu_update_tokeninfo, + NULL, + iasecc_emu_store_data, +#if 0 + iasecc_pkcs15init_select_id, /* ext_select_id */ + iasecc_pkcs15init_set_pin, + iasecc_pkcs15init_erase_application +#endif +}; + + +struct sc_pkcs15init_operations * +sc_pkcs15init_get_iasecc_ops(void) +{ + return &sc_pkcs15init_iasecc_operations; +} + +#endif /* ENABLE_OPENSSL */