/* * card-itacns.c: Support for Italian CNS * * Copyright (C) 2008-2010 Emanuele Pucciarelli * Copyright (C) 2005 ST Incard srl, Giuseppe Amato , * Copyright (C) 2002 Andreas Jellinghaus * Copyright (C) 2001 Juha Yrjölä * * 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 */ /* * Specifications for the development of this driver come from: * http://www.cnipa.gov.it/html/docs/CNS%20Functional%20Specification%201.1.5_11012010.pdf */ #include "internal.h" #include "cardctl.h" #include "itacns.h" #include #include #include #define ITACNS_MAX_PAYLOAD 0xff static const struct sc_card_operations *default_ops = NULL; static struct sc_card_operations itacns_ops; static struct sc_card_driver itacns_drv = { "Italian CNS", "itacns", &itacns_ops, NULL, 0, NULL }; /* * Card matching */ /* List of ATR's for "hard" matching. */ static const struct sc_atr_table itacns_atrs[] = { { "3b:f4:18:00:ff:81:31:80:55:00:31:80:00:c7", NULL, NULL, SC_CARD_TYPE_ITACNS_CIE_V1, 0, NULL}, { NULL, NULL, NULL, 0, 0, NULL} }; /* Output debug info */ #define matchdebug(idx, c) do { \ sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, \ "Matching %x against atr[%d] == %x", c, idx, atr[idx]); \ } while(0); /* Check that we are not looking at values beyond the ATR's length. * If we are, then the card does not match. */ #define itacns_atr_l(idx) do {if (idx >= card->atr.len) return 0;} while(0); /* Match byte exactly and increment index. */ #define itacns_atr_match(idx, c) do { \ itacns_atr_l(idx); \ matchdebug(idx, c); \ if (((u8)atr[idx]) != c) return 0; \ idx++; \ } while(0); /* Match masked bits and increment index. */ #define itacns_atr_mmatch(idx, c, mask) do { \ itacns_atr_l(idx); \ if ((((u8)atr[idx]) & mask) != c) return 0; \ idx ++; \ } while(0); /* Macro to access private driver data. */ #define DRVDATA(card) ((itacns_drv_data_t *) card->drv_data) static int itacns_match_cns_card(sc_card_t *card, unsigned int i) { unsigned char *atr = card->atr.value; sc_context_t *ctx; ctx = card->ctx; itacns_atr_match(i, 0x01); /* H7 */ i += 2; /* H8, H9 */ itacns_atr_match(i, 'C'); /* H10 */ itacns_atr_match(i, 'N'); /* H11 */ itacns_atr_match(i, 'S'); /* H12 */ /* H13 */ /* Version byte: h.l, h in the high nibble, l in the low nibble. */ if(card->driver) { DRVDATA(card)->cns_version = atr[i]; } /* Warn if the version is not 1.0. */ if(atr[i] != 0x10) { char version[8]; snprintf(version, sizeof(version), "%d.%d", (atr[i] >> 4) & 0x0f, atr[i] & 0x0f); sc_log(card->ctx, "CNS card version %s; no official specifications " "are published. Proceeding anyway.\n", version); } i++; itacns_atr_match(i, 0x31); /* H14 */ itacns_atr_match(i, 0x80); /* H15 */ card->type = SC_CARD_TYPE_ITACNS_CNS; return 1; } static int itacns_match_cie_card(sc_card_t *card, unsigned int i) { unsigned char *atr = card->atr.value; sc_context_t *ctx; ctx = card->ctx; itacns_atr_match(i, 0x02); /* H7 */ itacns_atr_match(i, 'I'); /* H8 */ itacns_atr_match(i, 'T'); /* H9 */ itacns_atr_match(i, 'I'); /* H10 */ itacns_atr_match(i, 'D'); /* H11 */ itacns_atr_match(i, 0x20); /* H12 */ itacns_atr_match(i, 0x20); /* H13 */ itacns_atr_match(i, 0x31); /* H14 */ itacns_atr_match(i, 0x80); /* H15 */ card->type = SC_CARD_TYPE_ITACNS_CIE_V2; return 1; } static int itacns_match_card(sc_card_t *card) { unsigned int i = 0; int r; unsigned char *atr = card->atr.value; int td1_idx; sc_context_t *ctx; ctx = card->ctx; /* Try table first */ r = _sc_match_atr(card, itacns_atrs, &card->type); if(r >= 0) return 1; /* The ATR was not recognized; try to match it according to the official specs. */ /* Check ATR up to byte H6 */ itacns_atr_match(i, 0x3b); /* TS */ itacns_atr_mmatch(i, 0x8f, 0x8f); /* T0 */ /* TA1, TB1, TC1 */ if(atr[1] & 0x40) i++; if(atr[1] & 0x20) i++; if(atr[1] & 0x10) i++; /* TD1 */ td1_idx = i; itacns_atr_mmatch(i, 0x81, 0x8f); /* TA2, TB2, TC2 */ if(atr[td1_idx] & 0x40) i++; if(atr[td1_idx] & 0x20) i++; if(atr[td1_idx] & 0x10) i++; /* TD2 */ itacns_atr_match(i, 0x31); i += 2; /* TA3, TB3 */ itacns_atr_match(i, 0x00); /* H1 */ itacns_atr_match(i, 0x6b); /* H2 */ /* Store interesting data */ if(card->driver) { DRVDATA(card)->ic_manufacturer_code = card->atr.value[i]; DRVDATA(card)->mask_manufacturer_code = card->atr.value[i+1]; DRVDATA(card)->os_version_h = card->atr.value[i+2]; DRVDATA(card)->os_version_l = card->atr.value[i+3]; } i += 4; /* H3, H4, H5, H6 */ /* Check final part. */ if (itacns_match_cns_card(card, i)) return 1; if (itacns_match_cie_card(card, i)) return 1; /* No card type was matched. */ return 0; } /* * Initialization and termination */ static int itacns_init(sc_card_t *card) { unsigned long flags; SC_FUNC_CALLED(card->ctx, 1); card->name = "CNS card"; card->cla = 0x00; card->drv_data = calloc(1, sizeof(itacns_drv_data_t)); if (!card->drv_data) return SC_ERROR_OUT_OF_MEMORY; /* Match ATR again to find the card data. */ itacns_match_card(card); /* Set up algorithm info. */ flags = SC_ALGORITHM_NEED_USAGE | SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASHES ; _sc_card_add_rsa_alg(card, 1024, flags, 0); return SC_SUCCESS; } static int itacns_finish(struct sc_card *card) { if(card->drv_data) { free(card->drv_data); } return 0; } /* * Restore the indicated SE */ static int itacns_restore_security_env(sc_card_t *card, int se_num) { sc_apdu_t apdu; int r; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; SC_FUNC_CALLED(card->ctx, 1); /* * The Italian CNS requires a 0-valued Lc byte at the end of the APDU * (see paragraph 13.14 of the Functional Specification), but since * it is invalid, we "cheat" and pretend it's a Le byte. * * For this workaround, we must allocate and supply a response buffer, * even though we know it will not be used (we don't even check it). */ sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x22, 0xF3, se_num); apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); apdu.le = 0; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, 1, r); } /* * Set the security context * Things get a little messy here. It seems you cannot do any * crypto without a security environment - but there isn't really * a way to specify the security environment in PKCS15. * What I'm doing here (for now) is to assume that for a key * object with ID 0xNN there is always a corresponding SE object * with the same ID. * XXX Need to find out how the Aladdin drivers do it. */ static int itacns_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { sc_apdu_t apdu; u8 data[3]; int key_id, r; /* Do not complain about se_num; the argument is part of the API. */ (void) se_num; assert(card != NULL && env != NULL); if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) { sc_log(card->ctx, "No or invalid key reference\n"); return SC_ERROR_INVALID_ARGUMENTS; } key_id = env->key_ref[0]; /* CIE v1 cards need to restore security environment 0x30; all the others so far want 0x03. */ r = itacns_restore_security_env(card, (card->type == SC_CARD_TYPE_ITACNS_CIE_V1 ? 0x30 : 0x03)); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0xF1, 0); switch (env->operation) { case SC_SEC_OPERATION_DECIPHER: apdu.p2 = 0xB8; break; case SC_SEC_OPERATION_SIGN: apdu.p2 = 0xB6; break; default: return SC_ERROR_INVALID_ARGUMENTS; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Setting sec env for key_id=%d\n", key_id); data[0] = 0x83; data[1] = 0x01; data[2] = key_id; apdu.lc = apdu.datalen = 3; apdu.data = data; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); LOG_TEST_RET(card->ctx, r, "Card returned error"); SC_FUNC_RETURN(card->ctx, 1, r); } /* * The 0x80 thing tells the card it's okay to search parent * directories as well for the referenced object. * This is necessary for some Italian CNS cards, and to be avoided * for others. Right now it seems that it is only needed with * cards by STIncard. */ static int itacns_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { data->flags |= SC_PIN_CMD_NEED_PADDING; /* Enable backtracking for STIncard cards. */ if(DRVDATA(card)->mask_manufacturer_code == ITACNS_MASKMAN_STINCARD) { data->pin_reference |= 0x80; } /* FIXME: the following values depend on what pin length was * used when creating the BS objects */ if (data->pin1.max_length == 0) data->pin1.max_length = 8; if (data->pin2.max_length == 0) data->pin2.max_length = 8; return default_ops->pin_cmd(card, data, tries_left); } static int itacns_read_binary(sc_card_t *card, unsigned int idx, u8 *buf, size_t count, unsigned long flags) { size_t already_read = 0; int requested; int r; while(1) { requested = count - already_read; if(requested > ITACNS_MAX_PAYLOAD) requested = ITACNS_MAX_PAYLOAD; r = default_ops->read_binary(card, idx+already_read, &buf[already_read], requested, flags); if(r < 0) return r; already_read += r; if (r == 0 || r < requested || already_read == count) { /* We have finished */ return already_read; } } } static int itacns_list_files(sc_card_t *card, u8 *buf, size_t buflen) { struct sc_card_operations *list_ops; if (DRVDATA(card) && (DRVDATA(card)->mask_manufacturer_code == ITACNS_MASKMAN_SIEMENS)) { list_ops = sc_get_cardos_driver()->ops; } else { list_ops = sc_get_incrypto34_driver()->ops; } return list_ops->list_files(card, buf, buflen); } static void add_acl_entry(sc_file_t *file, int op, u8 byte) { unsigned int method, key_ref = SC_AC_KEY_REF_NONE; switch (byte) { case 0x00: method = SC_AC_NONE; break; case 0xFF: case 0x66: method = SC_AC_NEVER; break; default: if (byte > 0x1F) { method = SC_AC_UNKNOWN; } else { method = SC_AC_CHV; key_ref = byte; } break; } sc_file_add_acl_entry(file, op, method, key_ref); } static const int df_acl[9] = { -1, /* LCYCLE (life cycle change) */ SC_AC_OP_UPDATE, /* UPDATE Objects */ SC_AC_OP_WRITE, /* APPEND Objects */ SC_AC_OP_INVALIDATE, /* DF */ SC_AC_OP_REHABILITATE, /* DF */ SC_AC_OP_DELETE, /* DF */ SC_AC_OP_WRITE, /* ADMIN DF */ SC_AC_OP_CREATE, /* Files */ -1 /* Reserved */ }; static const int ef_acl[9] = { SC_AC_OP_READ, /* Data */ SC_AC_OP_UPDATE, /* Data (write file content) */ SC_AC_OP_WRITE, /* */ SC_AC_OP_INVALIDATE, /* EF */ SC_AC_OP_REHABILITATE, /* EF */ SC_AC_OP_ERASE, /* (delete) EF */ /* XXX: ADMIN should be an ACL type of its own, or mapped * to erase */ SC_AC_OP_ERASE, /* ADMIN EF (modify meta information?) */ -1, /* INC (-> cyclic fixed files) */ -1 /* DEC */ }; static void parse_sec_attr(sc_file_t *file, const u8 *buf, size_t len) { size_t i; const int *idx; idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl; /* acl defaults to 0xFF if unspecified */ for (i = 0; i < 9; i++) { if (idx[i] != -1) { add_acl_entry(file, idx[i], (u8)((i < len) ? buf[i] : 0xFF)); } } } static int itacns_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file) { int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = default_ops->select_file(card, in_path, file); if (r >= 0 && file) { parse_sec_attr((*file), (*file)->sec_attr, (*file)->sec_attr_len); } LOG_FUNC_RETURN(card->ctx, r); } static int itacns_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) { sc_path_t path; sc_file_t *file; size_t len; int r; u8 rbuf[256]; if (!serial) return SC_ERROR_INVALID_ARGUMENTS; /* see if we have cached serial number */ if (card->serialnr.len) { memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } sc_log(card->ctx, "Reading EF_IDCarta.\n"); sc_format_path("3F0010001003", &path); r = sc_select_file(card, &path, &file); if (r != SC_SUCCESS) { return SC_ERROR_WRONG_CARD; } len = file->size; sc_file_free(file); //Returned file->size should be 16. //We choose to not consider it as critical, because some cards //do not return FCI/FCP templates that include the file size. //Notify abnormal length anyway. if (len != 16) { sc_log(card->ctx, "Unexpected file length of EF_IDCarta (%lu)\n", (unsigned long) len); } r = sc_read_binary(card, 0, rbuf, 256, 0); if ( r != 16 ) { return SC_ERROR_WRONG_CARD; } /* cache serial number */ memcpy(card->serialnr.value, rbuf, 16); card->serialnr.len = 16; /* copy and return serial number */ memcpy(serial, &card->serialnr, sizeof(*serial)); return SC_SUCCESS; } static int itacns_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { switch (cmd) { case SC_CARDCTL_GET_SERIALNR: return itacns_get_serialnr(card, ptr); } return SC_ERROR_NOT_SUPPORTED; } static struct sc_card_driver * sc_get_driver(void) { if (!default_ops) default_ops = sc_get_iso7816_driver()->ops; itacns_ops = *default_ops; itacns_ops.match_card = itacns_match_card; itacns_ops.init = itacns_init; itacns_ops.finish = itacns_finish; itacns_ops.set_security_env = itacns_set_security_env; itacns_ops.restore_security_env = itacns_restore_security_env; itacns_ops.pin_cmd = itacns_pin_cmd; itacns_ops.read_binary = itacns_read_binary; itacns_ops.list_files = itacns_list_files; itacns_ops.select_file = itacns_select_file; itacns_ops.card_ctl = itacns_card_ctl; return &itacns_drv; } struct sc_card_driver * sc_get_itacns_driver(void) { return sc_get_driver(); }