#include "internal.h"
#include "cardctl.h"
#include "itacns.h"
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#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
};
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}
};
#define matchdebug(idx, c) do { \
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, \
"Matching %x against atr[%d] == %x", c, idx, atr[idx]); \
} while(0);
#define itacns_atr_l(idx) do {if (idx >= card->atr.len) return 0;} while(0);
#define itacns_atr_match(idx, c) do { \
itacns_atr_l(idx); \
matchdebug(idx, c); \
if (((u8)atr[idx]) != c) return 0; \
idx++; \
} while(0);
#define itacns_atr_mmatch(idx, c, mask) do { \
itacns_atr_l(idx); \
if ((((u8)atr[idx]) & mask) != c) return 0; \
idx ++; \
} while(0);
#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);
i += 2;
itacns_atr_match(i, 'C');
itacns_atr_match(i, 'N');
itacns_atr_match(i, 'S');
if(card->driver) {
DRVDATA(card)->cns_version = atr[i];
}
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);
itacns_atr_match(i, 0x80);
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);
itacns_atr_match(i, 'I');
itacns_atr_match(i, 'T');
itacns_atr_match(i, 'I');
itacns_atr_match(i, 'D');
itacns_atr_match(i, 0x20);
itacns_atr_match(i, 0x20);
itacns_atr_match(i, 0x31);
itacns_atr_match(i, 0x80);
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;
r = _sc_match_atr(card, itacns_atrs, &card->type);
if(r >= 0) return 1;
itacns_atr_match(i, 0x3b);
itacns_atr_mmatch(i, 0x8f, 0x8f);
if(atr[1] & 0x40) i++;
if(atr[1] & 0x20) i++;
if(atr[1] & 0x10) i++;
td1_idx = i;
itacns_atr_mmatch(i, 0x81, 0x8f);
if(atr[td1_idx] & 0x40) i++;
if(atr[td1_idx] & 0x20) i++;
if(atr[td1_idx] & 0x10) i++;
itacns_atr_match(i, 0x31);
i += 2;
itacns_atr_match(i, 0x00);
itacns_atr_match(i, 0x6b);
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;
if (itacns_match_cns_card(card, i)) return 1;
if (itacns_match_cie_card(card, i)) return 1;
return 0;
}
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;
itacns_match_card(card);
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;
}
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);
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);
}
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;
(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];
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);
}
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;
if(DRVDATA(card)->mask_manufacturer_code == ITACNS_MASKMAN_STINCARD) {
data->pin_reference |= 0x80;
}
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) {
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,
SC_AC_OP_UPDATE,
SC_AC_OP_WRITE,
SC_AC_OP_INVALIDATE,
SC_AC_OP_REHABILITATE,
SC_AC_OP_DELETE,
SC_AC_OP_WRITE,
SC_AC_OP_CREATE,
-1
};
static const int ef_acl[9] = {
SC_AC_OP_READ,
SC_AC_OP_UPDATE,
SC_AC_OP_WRITE,
SC_AC_OP_INVALIDATE,
SC_AC_OP_REHABILITATE,
SC_AC_OP_ERASE,
SC_AC_OP_ERASE,
-1,
-1
};
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;
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;
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;
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;
}
memcpy(card->serialnr.value, rbuf, 16);
card->serialnr.len = 16;
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();
}