/*
 * PKCS15 emulation layer for 1202, 1203 and 1400 Infocamere card.
 * To see how this works, run p15dump on your Infocamere card.
 *
 * Copyright (C) 2005, Sirio Capizzi <graaf@virgilio.it>
 * Copyright (C) 2004, Antonino Iacono <ant_iacono@tin.it>
 * Copyright (C) 2003, Olaf Kirch <okir@suse.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <opensc/pkcs15.h>
#include <opensc/log.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <compat_strlcpy.h>

#ifdef ENABLE_ZLIB
#include <zlib.h>
#endif

int sc_pkcs15emu_infocamere_init_ex(sc_pkcs15_card_t *,
				    sc_pkcs15emu_opt_t *);

static int (*set_security_env) (sc_card_t *, const sc_security_env_t *,
				int);

static int set_sec_env(sc_card_t * card, const sc_security_env_t * env,
		       int se_num)
{
	sc_security_env_t tenv = *env;
	if (tenv.operation == SC_SEC_OPERATION_SIGN)
		tenv.operation = SC_SEC_OPERATION_DECIPHER;
	return set_security_env(card, &tenv, se_num);
}

static int do_sign(sc_card_t * card, const u8 * in, size_t inlen, u8 * out,
		   size_t outlen)
{
	return card->ops->decipher(card, in, inlen, out, outlen);
}

static void set_string(char **strp, const char *value)
{
	if (*strp)
		free(*strp);
	*strp = value ? strdup(value) : NULL;
}

#if 1
/* XXX: temporary copy of the old pkcs15emu functions,
 *      to be removed */
static int sc_pkcs15emu_add_pin(sc_pkcs15_card_t *p15card,
                const sc_pkcs15_id_t *id, const char *label,
                const sc_path_t *path, int ref, int type,
                unsigned int min_length,
                unsigned int max_length,
                int flags, int tries_left, const char pad_char, int obj_flags)
{
        sc_pkcs15_pin_info_t info;
	sc_pkcs15_object_t   obj;

	memset(&info, 0, sizeof(info));
	memset(&obj,  0, sizeof(obj));

        info.auth_id           = *id;
        info.min_length        = min_length;
        info.max_length        = max_length;
        info.stored_length     = max_length;
        info.type              = type;
        info.reference         = ref;
        info.flags             = flags;
        info.tries_left        = tries_left;
        info.magic             = SC_PKCS15_PIN_MAGIC;
        info.pad_char          = pad_char;

        if (path)
                info.path = *path;
        if (type == SC_PKCS15_PIN_TYPE_BCD)
                info.stored_length /= 2;

	strlcpy(obj.label, label, sizeof(obj.label));
	obj.flags = obj_flags;

        return sc_pkcs15emu_add_pin_obj(p15card, &obj, &info);
}

static int sc_pkcs15emu_add_prkey(sc_pkcs15_card_t *p15card,
                const sc_pkcs15_id_t *id,
                const char *label,
                int type, unsigned int modulus_length, int usage,
                const sc_path_t *path, int ref,
                const sc_pkcs15_id_t *auth_id, int obj_flags)
{
        sc_pkcs15_prkey_info_t info;
	sc_pkcs15_object_t     obj;

	memset(&info, 0, sizeof(info));
	memset(&obj,  0, sizeof(obj));

        info.id                = *id;
        info.modulus_length    = modulus_length;
        info.usage             = usage;
        info.native            = 1;
        info.access_flags      = SC_PKCS15_PRKEY_ACCESS_SENSITIVE
                                | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE
                                | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE
                                | SC_PKCS15_PRKEY_ACCESS_LOCAL;
        info.key_reference     = ref;

        if (path)
                info.path = *path;

	obj.flags = obj_flags;
	strlcpy(obj.label, label, sizeof(obj.label));
	if (auth_id != NULL)
		obj.auth_id = *auth_id;

        return sc_pkcs15emu_add_rsa_prkey(p15card, &obj, &info);
}

static int sc_pkcs15emu_add_cert(sc_pkcs15_card_t *p15card,
	int type, int authority, const sc_path_t *path,
	const sc_pkcs15_id_t *id, const char *label, int obj_flags)
{
	/* const char *label = "Certificate"; */
	sc_pkcs15_cert_info_t info;
	sc_pkcs15_object_t    obj;

	memset(&info, 0, sizeof(info));
	memset(&obj,  0, sizeof(obj));

	info.id                = *id;
	info.authority         = authority;
	if (path)
		info.path = *path;

	strlcpy(obj.label, label, sizeof(obj.label));
	obj.flags = obj_flags;

	return sc_pkcs15emu_add_x509_cert(p15card, &obj, &info);
}
#endif

static int infocamere_1200_init(sc_pkcs15_card_t * p15card)
{
	const int prkey_usage = SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
	const int authprkey_usage = SC_PKCS15_PRKEY_USAGE_SIGN
	    | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER
	    | SC_PKCS15_PRKEY_USAGE_ENCRYPT
	    | SC_PKCS15_PRKEY_USAGE_DECRYPT;

	sc_card_t *card = p15card->card;
	sc_path_t path;
	sc_file_t *file;
	sc_pkcs15_id_t id, auth_id;
	unsigned char buffer[256];
	unsigned char ef_gdo[256];
	char serial[256];
	unsigned char certlen[2];
	int authority, change_sign = 0;
	struct sc_pkcs15_cert_info cert_info;
        struct sc_pkcs15_object    cert_obj;

	const char *label = "User Non-repudiation Certificate";
	const char *calabel = "CA Certificate";
	const char *authlabel = "User Authentication Certificate";

	const char *infocamere_cert_path[2] = {
		"DF01C000",
		"3F00000011111A02"
	};

	const char *infocamere_auth_certpath[2] = {
		"11111A02",
		"000011111B02"
	};

	const char *infocamere_cacert_path[2] = {
		"DF01C008",
		"000011114101"
	};

	const char *infocamere_auth_path[2] = {
		"3F001111",
		"3F0000001111"
	};

	const char *infocamere_nrepud_path[2] = {
		"3F00DF01",
		"3F0000001111"
	};

	const int infocamere_idpin_auth_obj[2] = {
		0x95,
		0x81
	};

	const int infocamere_idpin_nrepud_obj[2] = {
		0x99,
		0x81
	};

	const int infocamere_idprkey_auth_obj[2] = {
		0x9B,
		0x01
	};

	const int infocamere_idprkey_nrepud_obj[2] = {
		0x84,
		0x01
	};

	const char *authPIN = "Authentication PIN";
	const char *nonrepPIN = "Non-repudiation PIN";

	const char *authPRKEY = "Authentication Key";
	const char *nonrepPRKEY = "Non repudiation Key";

	const int flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE |
	    SC_PKCS15_PIN_FLAG_INITIALIZED |
	    SC_PKCS15_PIN_FLAG_NEEDS_PADDING;

	int r, len_iccsn, len_chn;

	sc_format_path("3F002F02", &path);

	sc_ctx_suppress_errors_on(card->ctx);
	r = sc_select_file(card, &path, &file);
	sc_ctx_suppress_errors_off(card->ctx);
	
	if (r != SC_SUCCESS || file->size > 255) {
		/* Not EF.GDO */
		return SC_ERROR_WRONG_CARD;
	}

	sc_read_binary(card, 0, ef_gdo, file->size, 0);

	if (ef_gdo[0] != 0x5A || file->size < 3) {
		/* Not EF.GDO */
		return SC_ERROR_WRONG_CARD;
	}

	len_iccsn = ef_gdo[1];

	memcpy(buffer, ef_gdo + 2, len_iccsn);

	sc_bin_to_hex(buffer, len_iccsn, serial, sizeof(serial), 0);

	if (file->size < (size_t) (len_iccsn + 5)) {
		/* Not CHN */
		return SC_ERROR_WRONG_CARD;
	}

	if (!
	    (ef_gdo[len_iccsn + 2] == 0x5F
	     && ef_gdo[len_iccsn + 3] == 0x20)) {
		/* Not CHN */
		return SC_ERROR_WRONG_CARD;
	}

	len_chn = ef_gdo[len_iccsn + 4];

	if (len_chn < 2 || len_chn > 8) {
		/* Length CHN incorrect */
		return SC_ERROR_WRONG_CARD;
	}

	if (!
	    (ef_gdo[len_iccsn + 5] == 0x12
	     && (ef_gdo[len_iccsn + 6] == 0x02
		 || ef_gdo[len_iccsn + 6] == 0x03))) {
		/* Not Infocamere Card */
		return SC_ERROR_WRONG_CARD;
	}

	set_string(&p15card->serial_number, serial);

	if (ef_gdo[len_iccsn + 6] == 0x02)
		set_string(&p15card->label, "Infocamere 1202 Card");
	else {
		set_string(&p15card->label, "Infocamere 1203 Card");
		change_sign = 1;
	}

	set_string(&p15card->manufacturer_id, "Infocamere");

	authority = 0;

	/* Get the authentication certificate length */

	sc_format_path(infocamere_auth_certpath[ef_gdo[len_iccsn+6]-2], &path);

	sc_ctx_suppress_errors_on(card->ctx);
	r = sc_select_file(card, &path, NULL);
	sc_ctx_suppress_errors_off(card->ctx);

	if (r >= 0) {

		sc_read_binary(card, 0, certlen, 2, 0);

		/* Now set the certificate offset/len */

		path.index = 2;
		path.count = (certlen[1] << 8) + certlen[0];

		memset(&cert_info, 0, sizeof(cert_info));
                memset(&cert_obj,  0, sizeof(cert_obj));

		sc_pkcs15_format_id("1", &cert_info.id);
        	cert_info.authority = authority;
        	cert_info.path = path;
		strlcpy(cert_obj.label, authlabel, sizeof(cert_obj.label));
	        cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE;

		r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
        	if (r < 0)
        		return SC_ERROR_INTERNAL;

		/* XXX: the IDs for the key/pin in case of the 1203 type 
		 * are wrong, therefore I disable them for now -- Nils */
		if (!change_sign) {    
		/* add authentication PIN */

                sc_format_path(infocamere_auth_path[ef_gdo[len_iccsn+6]-2], &path);
                
		sc_pkcs15_format_id("1", &id);
		sc_pkcs15emu_add_pin(p15card, &id,
                                authPIN, &path, infocamere_idpin_auth_obj[ef_gdo[len_iccsn+6]-2],
                                SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
                                5, 8, flags, 3, 0, 
				SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE);

		/* add authentication private key */

		auth_id.value[0] = 1;
		auth_id.len = 1;

		sc_pkcs15emu_add_prkey(p15card, &id,
				authPRKEY,
				SC_PKCS15_TYPE_PRKEY_RSA, 
				1024, authprkey_usage,
				&path, infocamere_idprkey_auth_obj[ef_gdo[len_iccsn+6]-2],
				&auth_id, SC_PKCS15_CO_FLAG_PRIVATE);
		}

	}

	/* Get the non-repudiation certificate length */

	sc_format_path(infocamere_cert_path[ef_gdo[len_iccsn+6]-2], &path);

	if (sc_select_file(card, &path, NULL) < 0)
		{
		return SC_ERROR_INTERNAL;
		}

	sc_read_binary(card, 0, certlen, 2, 0);

	/* Now set the certificate offset/len */
	path.index = 2;
	path.count = (certlen[1] << 8) + certlen[0];
	
        memset(&cert_info, 0, sizeof(cert_info));
        memset(&cert_obj,  0, sizeof(cert_obj));

	sc_pkcs15_format_id("2", &cert_info.id);

        cert_info.authority = authority;
        cert_info.path = path;
        strlcpy(cert_obj.label, label, sizeof(cert_obj.label));
        cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE;

	r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
        if (r < 0)
        	return SC_ERROR_INTERNAL;

	/* Get the CA certificate length */

	authority = 1;

	sc_format_path(infocamere_cacert_path[ef_gdo[len_iccsn+6]-2], &path);

	sc_ctx_suppress_errors_on(card->ctx);
	r = sc_select_file(card, &path, NULL);
	sc_ctx_suppress_errors_off(card->ctx);

	if (r >= 0) {
		size_t len;

		sc_read_binary(card, 0, certlen, 2, 0);

		len = (certlen[1] << 8) + certlen[0];

		if (len != 0) {
			/* Now set the certificate offset/len */
			path.index = 2;
			path.count = len;

			memset(&cert_info, 0, sizeof(cert_info));
	                memset(&cert_obj,  0, sizeof(cert_obj));

			sc_pkcs15_format_id("3", &cert_info.id);
	        	cert_info.authority = authority;
	        	cert_info.path = path;
	        	strlcpy(cert_obj.label, calabel, sizeof(cert_obj.label));
		        cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE;

			r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
        		if (r < 0)
        		return SC_ERROR_INTERNAL;
		}
	}

        /* add non repudiation PIN */

	sc_format_path(infocamere_nrepud_path[ef_gdo[len_iccsn+6]-2], &path);

	sc_pkcs15_format_id("2", &id);
	sc_pkcs15emu_add_pin(p15card, &id,
		nonrepPIN, &path, infocamere_idpin_nrepud_obj[ef_gdo[len_iccsn+6]-2],
		SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 5, 8, flags, 3, 0,
		SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE);


	/* add non repudiation private key */

	auth_id.value[0] = 2;
	auth_id.len = 1;

	sc_pkcs15emu_add_prkey(p15card, &id, nonrepPRKEY,
			       SC_PKCS15_TYPE_PRKEY_RSA, 
                               1024, prkey_usage,
                               &path, infocamere_idprkey_nrepud_obj[ef_gdo[len_iccsn+6]-2],
                               &auth_id, SC_PKCS15_CO_FLAG_PRIVATE);


	/* return to MF */
	sc_format_path("3F00", &path);
	r = sc_select_file(card, &path, NULL);
	if (r != SC_SUCCESS)
		return r;

	if (change_sign) {
		/* save old signature funcs */
		set_security_env = card->ops->set_security_env;
		/* set new one */
		card->ops->set_security_env = set_sec_env;
		card->ops->compute_signature = do_sign;
	}

	return SC_SUCCESS;
}

static int infocamere_1400_set_sec_env(struct sc_card *card,
				       const struct sc_security_env *env,
				       int se_num)
{
	int r;

	struct sc_security_env tenv = *env;
	if (tenv.operation == SC_SEC_OPERATION_SIGN)
		tenv.operation = SC_SEC_OPERATION_DECIPHER;

	if ((r =
	     card->ops->restore_security_env(card, 0x40)) == SC_SUCCESS)
		return set_security_env(card, &tenv, se_num);
	else
		return r;
}

#ifdef ENABLE_ZLIB

static const u8 ATR_1400[] =
    { 0x3b, 0xfc, 0x98, 0x00, 0xff, 0xc1, 0x10, 0x31, 0xfe, 0x55, 0xc8,
	0x03, 0x49, 0x6e, 0x66, 0x6f, 0x63, 0x61, 0x6d, 0x65, 0x72, 0x65,
	0x28
};

/* Loads certificates.
* Certificates are stored in a ZLib compressed form with
* a 4 byte header, so we extract, decompress and cache
* them.
*/
static int loadCertificate(sc_pkcs15_card_t * p15card, int i,
			   const char *certPath, const char *certLabel)
{
	unsigned char *compCert = NULL, *cert = NULL, size[2];
	unsigned long int compLen, len;
	sc_pkcs15_cert_info_t cert_info;
	sc_pkcs15_object_t cert_obj;
	sc_path_t cpath;
	sc_card_t *card = p15card->card;
	sc_pkcs15_id_t id;
	int r;

	memset(&cert_info, 0, sizeof(cert_info));
	memset(&cert_obj, 0, sizeof(cert_obj));

	sc_format_path(certPath, &cpath);

	if (sc_select_file(card, &cpath, NULL) != SC_SUCCESS)
		return SC_ERROR_WRONG_CARD;

	sc_read_binary(card, 2, size, 2, 0);

	compLen = (size[0] << 8) + size[1];
	compCert =
	    (unsigned char *) malloc(compLen * sizeof(unsigned char));
	len = 4 * compLen;	/*Approximation of the uncompressed size */
	cert = (unsigned char *) malloc(len * sizeof(unsigned char));

	sc_read_binary(card, 4, compCert, compLen, 0);

	if ((r = uncompress(cert, &len, compCert, compLen)) != Z_OK) {
		sc_error(p15card->card->ctx, "Zlib error: %d", r);
		return SC_ERROR_INTERNAL;
	}

	cpath.index = 0;
	cpath.count = len;

	sc_pkcs15_cache_file(p15card, &cpath, cert, len);

	id.len=1;
	id.value[0] = i + 1;

	cert_info.id = id;
	cert_info.path = cpath;
	cert_info.authority = (i == 2);

	strlcpy(cert_obj.label, certLabel, sizeof(cert_obj.label));
	cert_obj.flags = SC_PKCS15_CO_FLAG_MODIFIABLE;

	sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);

	return SC_SUCCESS;
}


static int infocamere_1400_init(sc_pkcs15_card_t * p15card)
{
	sc_card_t *card = p15card->card;
	sc_path_t path;
	sc_pkcs15_id_t id, auth_id;
	unsigned char serial[16];
	int flags;
	int r;
	int hasAuthCert = 0;

	const char *certLabel[] = { "User Non-repudiation Certificate",
		"User Authentication Certificate",
		"CA Certificate"
	};

	const char *certPath[] =
	    { "300060000000", "300060000001", "300060000002" };

	const char *pinLabel[] =
	    { "Non-repudiation PIN", "Authentication PIN" };
	int retries[] = { 3, -1 };

	const char *keyPath[] = { "30004000001", "30004000002" };
	const char *keyLabel[] =
	    { "Non repudiation Key", "Authentication Key" };
	static int usage[] = { SC_PKCS15_PRKEY_USAGE_NONREPUDIATION,
		SC_PKCS15_PRKEY_USAGE_SIGN
		    | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER
		    | SC_PKCS15_PRKEY_USAGE_ENCRYPT
		    | SC_PKCS15_PRKEY_USAGE_DECRYPT
	};

	auth_id.len = 1;
	id.len = 1;

	/* OpenSC doesn't define constants to identify BSOs for
	 * restoring security environment, so we overload
	 * the set_security_env function to support restore_sec_env */
	set_security_env = card->ops->set_security_env;
	card->ops->set_security_env = infocamere_1400_set_sec_env;
	card->ops->compute_signature = do_sign;
	p15card->opts.use_cache = 1;

	sc_format_path("30000001", &path);

	r = sc_select_file(card, &path, NULL);
	
	if (r != SC_SUCCESS)
		return SC_ERROR_WRONG_CARD;

	sc_read_binary(card, 15, serial, 15, 0);
	serial[15] = '\0';

	set_string(&p15card->serial_number, (char *)serial);
	set_string(&p15card->label, "Infocamere 1400 Card");
	set_string(&p15card->manufacturer_id, "Infocamere");

	if ((r = loadCertificate(p15card, 0, certPath[0], certLabel[0])) !=
	    SC_SUCCESS) {
		sc_error(p15card->card->ctx, "%s", sc_strerror(r));
		return SC_ERROR_WRONG_CARD;
	}

	hasAuthCert =
	    loadCertificate(p15card, 1, certPath[1],
			    certLabel[1]) == SC_SUCCESS;
	loadCertificate(p15card, 2, certPath[2], certLabel[2]);

	flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE |
	    SC_PKCS15_PIN_FLAG_INITIALIZED |
	    SC_PKCS15_PIN_FLAG_NEEDS_PADDING;

	/* adding PINs & private keys */

	sc_format_path("30004000", &path);
	id.value[0] = 1;

	sc_pkcs15emu_add_pin(p15card, &id,
			     pinLabel[0], &path, 1,
			     SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
			     5, 8, flags, retries[0], 0,
			     SC_PKCS15_CO_FLAG_MODIFIABLE |
			     SC_PKCS15_CO_FLAG_PRIVATE);

	sc_format_path(keyPath[0], &path);
	auth_id.value[0] = 1;
	sc_pkcs15emu_add_prkey(p15card, &id,
			       keyLabel[0],
			       SC_PKCS15_TYPE_PRKEY_RSA,
			       1024, usage[0],
			       &path, 1,
			       &auth_id, SC_PKCS15_CO_FLAG_PRIVATE);


	if (hasAuthCert) {
		sc_format_path("30004000", &path);
		id.value[0] = 2;

		sc_pkcs15emu_add_pin(p15card, &id,
				     pinLabel[1], &path, 2,
				     SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
				     5, 8, flags, retries[1], 0,
				     SC_PKCS15_CO_FLAG_MODIFIABLE |
				     SC_PKCS15_CO_FLAG_PRIVATE);

		sc_format_path(keyPath[1], &path);
		auth_id.value[0] = 2;
		sc_pkcs15emu_add_prkey(p15card, &id,
				       keyLabel[1],
				       SC_PKCS15_TYPE_PRKEY_RSA,
				       1024, usage[1],
				       &path, 2,
				       &auth_id,
				       SC_PKCS15_CO_FLAG_PRIVATE);
	}

	/* return to MF */
	sc_format_path("3F00", &path);
	r = sc_select_file(card, &path, NULL);
	return r;
	}

#endif

static const u8 ATR_1600[] = { 0x3B, 0xF4, 0x98, 0x00, 0xFF, 0xC1, 0x10,
	0x31, 0xFE, 0x55, 0x4D, 0x34, 0x63, 0x76, 0xB4
};

static int infocamere_1600_init(sc_pkcs15_card_t * p15card)
{
	sc_card_t *card = p15card->card;
	sc_path_t path;
	sc_pkcs15_id_t id, auth_id;
	unsigned char serial[17];
	int flags;
	int r;
	int hasAuthCert = 0;

	const char *certLabel[] = { "User Non-repudiation Certificate",
		"User Authentication Certificate"
	};

	const char *certPath[] = { "200020010008", "20002001000E" };

	const char *pinLabel[] =
	    { "Non-repudiation PIN", "Authentication PIN" };
	int retries[] = { 3, -1 };

	const char *keyPath[] = { "200020010004", "20002001000A" };
	const char *keyLabel[] =
	    { "Non repudiation Key", "Authentication Key" };
	static int usage[] = { SC_PKCS15_PRKEY_USAGE_NONREPUDIATION,
		SC_PKCS15_PRKEY_USAGE_SIGN
		    | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER
		    | SC_PKCS15_PRKEY_USAGE_ENCRYPT
		    | SC_PKCS15_PRKEY_USAGE_DECRYPT
	};

	auth_id.len = 1;
	id.len = 1;

	/* OpenSC doesn't define constants to identify BSOs for
	 * restoring security environment, so we overload
	 * the set_security_env function to support restore_sec_env */
	set_security_env = card->ops->set_security_env;
	card->ops->set_security_env = infocamere_1400_set_sec_env;
	card->ops->compute_signature = do_sign;

	sc_format_path("200020012002", &path);

	r = sc_select_file(card, &path, NULL);

	if (r != SC_SUCCESS)
		return SC_ERROR_WRONG_CARD;

	sc_read_binary(card, 30, serial, 16, 0);
	serial[16] = '\0';

	set_string(&p15card->serial_number, (char *) serial);
	set_string(&p15card->label, "Infocamere 1600 Card");
	set_string(&p15card->manufacturer_id, "Infocamere");

	/* Adding certificates.
	 * Certificates are stored in a ZLib compressed form with
	 * a 4 byte header, so we extract, decompress and cache
	 * them.
	 */
	sc_format_path(certPath[0], &path);
	if (sc_select_file(card, &path, NULL) != SC_SUCCESS)
		return SC_ERROR_WRONG_CARD;

	id.value[0] = 1;

	sc_pkcs15emu_add_cert(p15card,
			      SC_PKCS15_TYPE_CERT_X509, 0,
			      &path, &id, certLabel[0],
			      SC_PKCS15_CO_FLAG_MODIFIABLE);

	sc_format_path(certPath[1], &path);
	if (sc_select_file(card, &path, NULL) == SC_SUCCESS) {
		hasAuthCert = 1;

		id.value[0] = 2;

		sc_pkcs15emu_add_cert(p15card,
				      SC_PKCS15_TYPE_CERT_X509, 1,
				      &path, &id, certLabel[1],
				      SC_PKCS15_CO_FLAG_MODIFIABLE);
	}

	flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE |
	    SC_PKCS15_PIN_FLAG_INITIALIZED |
	    SC_PKCS15_PIN_FLAG_NEEDS_PADDING;

	/* adding PINs & private keys */
	sc_format_path("2000", &path);
	id.value[0] = 1;

	sc_pkcs15emu_add_pin(p15card, &id,
			     pinLabel[0], &path, 1,
			     SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
			     5, 8, flags, retries[0], 0,
			     SC_PKCS15_CO_FLAG_MODIFIABLE |
			     SC_PKCS15_CO_FLAG_PRIVATE);

	sc_format_path(keyPath[0], &path);
	auth_id.value[0] = 1;
	sc_pkcs15emu_add_prkey(p15card, &id,
			       keyLabel[0],
			       SC_PKCS15_TYPE_PRKEY_RSA,
			       1024, usage[0],
			       &path, 1,
			       &auth_id, SC_PKCS15_CO_FLAG_PRIVATE);

	if (hasAuthCert) {
		id.value[0] = 2;

		sc_pkcs15emu_add_pin(p15card, &id,
				     pinLabel[1], &path, 2,
				     SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
				     5, 8, flags, retries[1], 0,
				     SC_PKCS15_CO_FLAG_MODIFIABLE |
				     SC_PKCS15_CO_FLAG_PRIVATE);

		sc_format_path(keyPath[1], &path);
		auth_id.value[0] = 2;
		sc_pkcs15emu_add_prkey(p15card, &id,
				       keyLabel[1],
				       SC_PKCS15_TYPE_PRKEY_RSA,
				       1024, usage[1],
				       &path, 2,
				       &auth_id,
				       SC_PKCS15_CO_FLAG_PRIVATE);
	}

	/* return to MF */
	sc_format_path("3F00", &path);
	r = sc_select_file(card, &path, NULL);

	return SC_SUCCESS;
}

static int infocamere_detect_card(sc_pkcs15_card_t * p15card)
{
	sc_card_t *card = p15card->card;

	/* check if we have the correct card OS */
	if (strcmp(card->name, "STARCOS SPK 2.3")
	    && strcmp(card->name, "CardOS M4"))
		return SC_ERROR_WRONG_CARD;
	return SC_SUCCESS;
}

int sc_pkcs15emu_infocamere_init_ex(sc_pkcs15_card_t * p15card,
				    sc_pkcs15emu_opt_t * opts)
{

	if (!(opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)) {
		if (infocamere_detect_card(p15card))
			return SC_ERROR_WRONG_CARD;
	}

	if (memcmp(p15card->card->atr, ATR_1600, sizeof(ATR_1600)) == 0)
		return infocamere_1600_init(p15card);
#ifdef ENABLE_ZLIB
	else if (memcmp(p15card->card->atr, ATR_1400, sizeof(ATR_1400)) ==
		 0)
		return infocamere_1400_init(p15card);
#endif
	else
		return infocamere_1200_init(p15card);

}
