static int cardos_have_2048bit_package(sc_card_t *card)
{
	sc_apdu_t apdu;
        u8        rbuf[SC_MAX_APDU_BUFFER_SIZE];
        int       r;
	const u8  *p = rbuf, *q;
	size_t    len, tlen = 0, ilen = 0;

	sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x88);
	apdu.resp    = rbuf;
	apdu.resplen = sizeof(rbuf);
	apdu.lc = 0;
	apdu.le = 256;
	r = sc_transmit_apdu(card, &apdu);
	SC_TEST_RET(card->ctx, r, "APDU transmit failed");

	if ((len = apdu.resplen) == 0)
		/* looks like no package has been installed  */
		return 0;

	while (len != 0) {
		p = sc_asn1_find_tag(card->ctx, p, len, 0xe1, &tlen);
		if (p == NULL)
			return 0;
		q = sc_asn1_find_tag(card->ctx, p, tlen, 0x01, &ilen);
		if (q == NULL || ilen != 4)
			return 0;
		if (q[0] == 0x1c)
			return 1;
		p   += tlen;
		len -= tlen + 2;
	}

	return 0;
}
Пример #2
0
/* Return the SE number from the keyD for the FID.  If ref_data is not
   NULL the reference data is returned; this shoudl be an array of at
   least 2 bytes.  Returns -1 on error.  */
static int get_se_num_from_keyd(sc_card_t * card, unsigned short fid,
				u8 * ref_data)
{
	sc_context_t *ctx = card->ctx;
	struct df_info_s *dfi;
	struct keyd_record_s *keyd;
	size_t len, taglen;
	const u8 *p, *tag;
	char dbgbuf[2048];
	u8 fidbuf[2];

	fidbuf[0] = fid >> 8;
	fidbuf[1] = fid;

	dfi = get_df_info(card);
	if (!dfi || !dfi->keyd_file) {
		sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "EF_keyD not loaded\n");
		return -1;
	}

	for (keyd = dfi->keyd_file; keyd; keyd = keyd->next) {
		p = keyd->data;
		len = keyd->datalen;

		sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL,
			p, len, dbgbuf, sizeof dbgbuf);
		sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "keyd no %d:\n%s", keyd->recno, dbgbuf);

		tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen);
		if (!tag || taglen != 4 ||
		    !(tag[2] == fidbuf[0] && tag[3] == fidbuf[1]))
			continue;
		/* Found a matching record. */
		if (ref_data) {
			ref_data[0] = tag[0];
			ref_data[1] = tag[1];
		}
		/* Look for the SE-DO */
		tag = sc_asn1_find_tag(ctx, p, len, 0x7B, &taglen);
		if (!tag || !taglen)
			continue;
		p = tag;
		len = taglen;
		/* And now look for the referenced SE. */
		tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen);
		if (!tag || taglen != 1)
			continue;
		return *tag;	/* found. */
	}
	sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "EF_keyD for %04hx not found\n", fid);
	return -1;
}
Пример #3
0
static int cardos_have_verifyrc_package(sc_card_t *card)
{
	sc_apdu_t apdu;
        u8        rbuf[SC_MAX_APDU_BUFFER_SIZE];
        int       r;
	const u8  *p = rbuf, *q;
	size_t    len, tlen = 0, ilen = 0;

	sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x88);
	apdu.resp    = rbuf;
	apdu.resplen = sizeof(rbuf);
	apdu.lc = 0;
	apdu.le = 256;
	r = sc_transmit_apdu(card, &apdu);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");

	if ((len = apdu.resplen) == 0)
		/* looks like no package has been installed  */
		return 0;

	while (len != 0) {
		p = sc_asn1_find_tag(card->ctx, p, len, 0xe1, &tlen);
		if (p == NULL)
			return 0;
		if (card->type == SC_CARD_TYPE_CARDOS_M4_3)	{
			/* the verifyRC package on CardOS 4.3B use Manufacturer ID 0x01	*/
			/* and Package Number 0x07					*/
			q = sc_asn1_find_tag(card->ctx, p, tlen, 0x01, &ilen);
			if (q == NULL || ilen != 4)
				return 0;
			if (q[0] == 0x07)
				return 1;
		} else if (card->type == SC_CARD_TYPE_CARDOS_M4_4)	{
			/* the verifyRC package on CardOS 4.4 use Manufacturer ID 0x03	*/
			/* and Package Number 0x02					*/
			q = sc_asn1_find_tag(card->ctx, p, tlen, 0x03, &ilen);
			if (q == NULL || ilen != 4)
				return 0;
			if (q[0] == 0x02)
				return 1;
		} else	{
			return 0;
		}
		p   += tlen;
		len -= tlen + 2;
	}

	return 0;
}
Пример #4
0
static int gemsafe_process_fci(struct sc_card *card, struct sc_file *file,
                               const u8 *buf, size_t len)
{
    int        r;
    size_t     tlen;
    const u8   *tag = NULL, *p = buf;
    const char *type;
    struct sc_context *ctx = card->ctx;

    SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);

    r = iso_ops->process_fci(card, file, buf, len);
    if (r < 0)
        return r;
    sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
             "processing GemSAFE V1 specific FCI information\n");


    tag = sc_asn1_find_tag(ctx, p, len, 0x82, &tlen);
    if (!tag) {
        /* no FDB => we have a DF */
        type = "DF";
        file->type = SC_FILE_TYPE_DF;
    } else {
        type = "EF";
        file->type = SC_FILE_TYPE_WORKING_EF;
    }

    sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "file type: %s\n", type);

    tag = sc_asn1_find_tag(ctx, p, len, 0x8C, &tlen);
    if (tag) {
        r = gemsafe_setacl(card, file, tag, strcmp(type, "DF") ? 0 : 1);
        if (r < 0) {
            sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "unable to set ACL\n");
            return SC_ERROR_INTERNAL;
        }
    } else
        sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "error: AM and SC bytes missing\n");

    return SC_SUCCESS;
}
Пример #5
0
/*
 * Extract a key component from the public key file populated by
 * GENERATE KEY PAIR
 */
static int parse_ext_pubkey_file(sc_card_t *card, const u8 *data, size_t len,
	sc_pkcs15_pubkey_t *pubkey)
{
	const u8     *p;
	size_t       ilen = 0, tlen = 0;

	if (data == NULL || len < 32)
		return SC_ERROR_INVALID_ARGUMENTS;
	data = sc_asn1_find_tag(card->ctx, data, len, 0x7f49, &ilen);
	if (data == NULL) {
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid public key data: missing tag");
		return SC_ERROR_INTERNAL;
	}

	p = sc_asn1_find_tag(card->ctx, data, ilen, 0x81, &tlen);
	if (p == NULL) {
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid public key data: missing modulus");
		return SC_ERROR_INTERNAL;
	}
	pubkey->u.rsa.modulus.len  = tlen;
	pubkey->u.rsa.modulus.data = malloc(tlen);
	if (pubkey->u.rsa.modulus.data == NULL)
		return SC_ERROR_OUT_OF_MEMORY;
	memcpy(pubkey->u.rsa.modulus.data, p, tlen);

	p = sc_asn1_find_tag(card->ctx, data, ilen, 0x82, &tlen);
	if (p == NULL) {
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid public key data: missing exponent");
		return SC_ERROR_INTERNAL;
	}
	pubkey->u.rsa.exponent.len  = tlen;
	pubkey->u.rsa.exponent.data = malloc(tlen);
	if (pubkey->u.rsa.exponent.data == NULL)
		return SC_ERROR_OUT_OF_MEMORY;
	memcpy(pubkey->u.rsa.exponent.data, p, tlen);

	return SC_SUCCESS; 
}
Пример #6
0
static int process_fci(sc_context_t *ctx, sc_file_t *file,
                       const u8 *buf, size_t buflen)
{
    /* NOTE: According to the Starcos S 2.1 manual it's possible
     *       that a SELECT DF returns as a FCI arbitrary data which
     *       is stored in a object file (in the corresponding DF)
     *       with the tag 0x6f.
     */

    size_t taglen, len = buflen;
    const u8 *tag = NULL, *p;

    sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "processing FCI bytes\n");

    if (buflen < 2)
        return SC_ERROR_INTERNAL;
    if (buf[0] != 0x6f)
        return SC_ERROR_INVALID_DATA;
    len = (size_t)buf[1];
    if (buflen - 2 < len)
        return SC_ERROR_INVALID_DATA;
    p = buf + 2;

    /* defaults */
    file->type = SC_FILE_TYPE_WORKING_EF;
    file->ef_structure = SC_FILE_EF_UNKNOWN;
    file->shareable = 0;
    file->record_length = 0;
    file->size = 0;

    tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen);
    if (tag != NULL && taglen >= 2) {
        int bytes = (tag[0] << 8) + tag[1];
        sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
                 "  bytes in file: %d\n", bytes);
        file->size = bytes;
    }

    tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen);
    if (tag != NULL) {
        const char *type = "unknown";
        const char *structure = "unknown";

        if (taglen == 1 && tag[0] == 0x01) {
            /* transparent EF */
            type = "working EF";
            structure = "transparent";
            file->type = SC_FILE_TYPE_WORKING_EF;
            file->ef_structure = SC_FILE_EF_TRANSPARENT;
        } else if (taglen == 1 && tag[0] == 0x11) {
            /* object EF */
            type = "working EF";
            structure = "object";
            file->type = SC_FILE_TYPE_WORKING_EF;
            file->ef_structure = SC_FILE_EF_TRANSPARENT; /* TODO */
        } else if (taglen == 3 && tag[1] == 0x21) {
            type = "working EF";
            file->record_length = tag[2];
            file->type = SC_FILE_TYPE_WORKING_EF;
            /* linear fixed, cyclic or compute */
            switch ( tag[0] )
            {
            case 0x02:
                structure = "linear fixed";
                file->ef_structure = SC_FILE_EF_LINEAR_FIXED;
                break;
            case 0x07:
                structure = "cyclic";
                file->ef_structure = SC_FILE_EF_CYCLIC;
                break;
            case 0x17:
                structure = "compute";
                file->ef_structure = SC_FILE_EF_UNKNOWN;
                break;
            default:
                structure = "unknown";
                file->ef_structure = SC_FILE_EF_UNKNOWN;
                file->record_length = 0;
                break;
            }
        }

        sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
                 "  type: %s\n", type);
        sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
                 "  EF structure: %s\n", structure);
    }
    file->magic = SC_FILE_MAGIC;

    return SC_SUCCESS;
}
Пример #7
0
static void process_fcp(sc_card_t * card, sc_file_t * file,
			const u8 * buf, size_t buflen)
{
	sc_context_t *ctx = card->ctx;
	size_t taglen, len = buflen;
	const u8 *tag = NULL, *p = buf;
	int bad_fde = 0;

	sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "processing FCI bytes\n");

	/* File identifier. */
	tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen);
	if (tag != NULL && taglen == 2) {
		file->id = (tag[0] << 8) | tag[1];
		sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
			"  file identifier: 0x%02X%02X\n", tag[0], tag[1]);
	}
	/* Number of data bytes in the file including structural information. */
	tag = sc_asn1_find_tag(ctx, p, len, 0x81, &taglen);
	if (!tag) {
		/* My card does not encode the filelength in 0x81 but
		   in 0x85 which is the file descriptor extension in TCOS.
		   Assume that this is the case when the regular file
		   size tag is not encoded. */
		tag = sc_asn1_find_tag(ctx, p, len, 0x85, &taglen);
		bad_fde = !!tag;
	}
	if (tag != NULL && taglen >= 2) {
		int bytes = (tag[0] << 8) + tag[1];
		sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
			"  bytes in file: %d\n", bytes);
		file->size = bytes;
	}
	if (tag == NULL) {
		tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen);
		if (tag != NULL && taglen >= 2) {
			int bytes = (tag[0] << 8) + tag[1];
			sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
				"  bytes in file: %d\n", bytes);
			file->size = bytes;
		}
	}

	/* File descriptor byte(s). */
	tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen);
	if (tag != NULL) {
		/* Fixme, this might actual be up to 6 bytes. */
		if (taglen > 0) {
			unsigned char byte = tag[0];
			const char *type;

			file->shareable = byte & 0x40 ? 1 : 0;
			sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
				"  shareable: %s\n",
				 (byte & 0x40) ? "yes" : "no");
			file->ef_structure = byte & 0x07;
			switch ((byte >> 3) & 7) {
			case 0:
				type = "working EF";
				file->type = SC_FILE_TYPE_WORKING_EF;
				break;
			case 1:
				type = "internal EF";
				file->type = SC_FILE_TYPE_INTERNAL_EF;
				break;
			case 7:
				type = "DF";
				file->type = SC_FILE_TYPE_DF;
				break;
			default:
				type = "unknown";
				break;
			}
			sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
				"  type: %s\n", type);
			sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
				"  EF structure: %d\n", byte & 0x07);
		}
	}
Пример #8
0
				type = "DF";
				file->type = SC_FILE_TYPE_DF;
				break;
			default:
				type = "unknown";
				break;
			}
			sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
				"  type: %s\n", type);
			sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
				"  EF structure: %d\n", byte & 0x07);
		}
	}

	/* DF name. */
	tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen);
	if (tag != NULL && taglen > 0 && taglen <= 16) {
		char name[17];
		size_t i;

		memcpy(file->name, tag, taglen);
		file->namelen = taglen;

		for (i = 0; i < taglen; i++) {
			if (isalnum(tag[i]) || ispunct(tag[i])
			    || isspace(tag[i]))
				name[i] = tag[i];
			else
				name[i] = '?';
		}
		name[taglen] = 0;
Пример #9
0
static int
iso7816_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, len = buflen;
	int i;
	const unsigned char *tag = NULL, *p = buf;

	sc_log(ctx, "processing FCI bytes");
	tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen);
	if (tag != NULL && taglen == 2) {
		file->id = (tag[0] << 8) | tag[1];
		sc_log(ctx, "  file identifier: 0x%02X%02X", tag[0], tag[1]);
	}

	/* determine the file size */
	/* try the tag 0x80 then the tag 0x81 */
	file->size = 0;
	for (i = 0x80; i <= 0x81; i++) {
		int size = 0;
		len = buflen;
		tag = sc_asn1_find_tag(ctx, p, len, i, &taglen);
		if (tag == NULL)
			continue;
		if (taglen == 0)
			continue;
		if (sc_asn1_decode_integer(tag, taglen, &size) < 0)
			continue;
		if (size <0)
			continue;

		file->size = size;
		sc_log(ctx, "  bytes in file: %d", file->size);
		break;
	}

	tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen);
	if (tag != NULL) {
		if (taglen > 0) {
			unsigned char byte = tag[0];
			const char *type;

			file->shareable = byte & 0x40 ? 1 : 0;
			sc_log(ctx, "  shareable: %s", (byte & 0x40) ? "yes" : "no");
			file->ef_structure = byte & 0x07;
			switch ((byte >> 3) & 7) {
			case 0:
				type = "working EF";
				file->type = SC_FILE_TYPE_WORKING_EF;
				break;
			case 1:
				type = "internal EF";
				file->type = SC_FILE_TYPE_INTERNAL_EF;
				break;
			case 7:
				type = "DF";
				file->type = SC_FILE_TYPE_DF;
				break;
			default:
				type = "unknown";
				break;
			}
			sc_log(ctx, "  type: %s", type);
			sc_log(ctx, "  EF structure: %d", byte & 0x07);
		}
	}
static int cardos_list_files(sc_card_t *card, u8 *buf, size_t buflen)
{
	sc_apdu_t apdu;
	u8        rbuf[256], offset = 0;
	const u8  *p = rbuf, *q;
	int       r;
	size_t    fids = 0, len;

	SC_FUNC_CALLED(card->ctx, 1);

	/* 0x16: DIRECTORY */
	/* 0x02: list both DF and EF */

get_next_part:
	sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x16, 0x02, offset);
	apdu.cla = 0x80;
	apdu.le = 256;
	apdu.resplen = 256;
	apdu.resp = rbuf;

	r = sc_transmit_apdu(card, &apdu);
	SC_TEST_RET(card->ctx, r, "APDU transmit failed");
	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	SC_TEST_RET(card->ctx, r, "DIRECTORY command returned error");

	if (apdu.resplen > 256) {
		sc_error(card->ctx, "directory listing > 256 bytes, cutting");
		r = 256;
	}

	len = apdu.resplen;
	while (len != 0) {
		size_t   tlen = 0, ilen = 0;
		/* is there a file informatin block (0x6f) ? */
		p = sc_asn1_find_tag(card->ctx, p, len, 0x6f, &tlen);
		if (p == NULL) {
			sc_error(card->ctx, "directory tag missing");
			return SC_ERROR_INTERNAL;
		}
		if (tlen == 0)
			/* empty directory */
			break;
		q = sc_asn1_find_tag(card->ctx, p, tlen, 0x86, &ilen);
		if (q == NULL || ilen != 2) {
			sc_error(card->ctx, "error parsing file id TLV object");
			return SC_ERROR_INTERNAL;
		}
		/* put file id in buf */
		if (buflen >= 2) {
			buf[fids++] = q[0];
			buf[fids++] = q[1];
			buflen -= 2;
		} else
			/* not enought space left in buffer => break */
			break;
		/* extract next offset */
		q = sc_asn1_find_tag(card->ctx, p, tlen, 0x8a, &ilen);
		if (q != NULL && ilen == 1) {
			offset = (u8)ilen;
			if (offset != 0)
				goto get_next_part;
		}
		len -= tlen + 2;
		p   += tlen;
	}

	r = fids;

	SC_FUNC_RETURN(card->ctx, 1, r);
}
static int process_fci(struct sc_context *ctx, struct sc_file *file,
		       const u8 *buf, size_t buflen)
{

	size_t taglen, len = buflen;
	const u8 *tag = NULL, *p;
  
	if (ctx->debug >= 3)
		sc_debug(ctx, "processing FCI bytes\n");

	if (buflen < 2)
		return SC_ERROR_INTERNAL;
	if (buf[0] != 0x6f)					/* FCI template */
		return SC_ERROR_INVALID_DATA;
	len = (size_t)buf[1];
	if (buflen - 2 < len)
		return SC_ERROR_INVALID_DATA;
	p = buf + 2;

	/* defaults */
	file->type = SC_FILE_TYPE_WORKING_EF;
	file->ef_structure = SC_FILE_EF_UNKNOWN;
	file->shareable = 0;
	file->record_length = 0;
	file->size = 0;
  
	/* get file size */
	tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen);
	if (tag != NULL && taglen >= 2) {
		int bytes = (tag[0] << 8) + tag[1];
		if (ctx->debug >= 3)
			sc_debug(ctx, "  bytes in file: %d\n", bytes);
		file->size = bytes;
	}

	/* get file type */
  	tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen);
	if (tag != NULL) {
		const char *type = "unknown";
		const char *structure = "unknown";

		if (taglen == 1 && tag[0] == 0x01) {
			/* transparent EF */
			type = "working EF";
			structure = "transparent";
			file->type = SC_FILE_TYPE_WORKING_EF;
			file->ef_structure = SC_FILE_EF_TRANSPARENT;
		} else if (taglen == 1 && tag[0] == 0x11) {
			/* object EF */
			type = "working EF";
			structure = "object";
			file->type = SC_FILE_TYPE_WORKING_EF;
			file->ef_structure = SC_FILE_EF_TRANSPARENT; /* TODO */
		} else if (taglen == 3 && tag[1] == 0x21) {
			type = "working EF";
			file->record_length = tag[2];
			file->type = SC_FILE_TYPE_WORKING_EF;
			/* linear fixed, cyclic or compute */
			switch ( tag[0] )
			{
				case 0x02:
					structure = "linear fixed";
					file->ef_structure = SC_FILE_EF_LINEAR_FIXED;
					break;
				case 0x07:
					structure = "cyclic";
					file->ef_structure = SC_FILE_EF_CYCLIC;
					break;
				case 0x17:
					structure = "compute";
					file->ef_structure = SC_FILE_EF_UNKNOWN;
					break;
				default:
					structure = "unknown";
					file->ef_structure = SC_FILE_EF_UNKNOWN;
					file->record_length = 0;
					break;
			}
		}

		if (ctx->debug >= 3) {
	 		sc_debug(ctx, "  type: %s\n", type);
			sc_debug(ctx, "  EF structure: %s\n", structure);
		}
	}
	file->magic = SC_FILE_MAGIC;

	return SC_SUCCESS;
}
Пример #12
0
/*
 * @brief Generate a private key on the card.
 */
static int
isoApplet_ctl_generate_key(sc_card_t *card, sc_cardctl_isoApplet_genkey_t *args)
{
	int r;
	sc_apdu_t apdu;
	u8 rbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
	u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
	u8 *p;
	const u8 *inner_tag_value;
	const u8 *outer_tag_value;
	unsigned int tag;
	size_t outer_tag_len;
	size_t inner_tag_len;
	unsigned int cla;

	LOG_FUNC_CALLED(card->ctx);

	/* MANAGE SECURITY ENVIRONMENT (SET). Set the algorithm and key references. */
	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0x00);

	p = sbuf;
	*p++ = 0x80; /* algorithm reference */
	*p++ = 0x01;
	*p++ = args->algorithm_ref;

	*p++ = 0x84; /* Private key reference */
	*p++ = 0x01;
	*p++ = args->priv_key_ref;

	r = p - sbuf;
	p = NULL;

	apdu.lc = r;
	apdu.datalen = r;
	apdu.data = sbuf;

	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");


	/* GENERATE ASYMMETRIC KEY PAIR
	 * We use a larger buffer here, even if the card does not support extended apdus.
	 * There are two cases:
	 *  1) The card can do ext. apdus: The data fits in one apdu.
	 *  2) The card can't do ext. apdus: sc_transmit_apdu will handle that - the
	 *     card will send SW_BYTES_REMAINING, OpenSC will automaticall do a
	 *     GET RESPONSE to get the remaining data, and will append it to the data
	 *     buffer. */
	if(args->algorithm_ref == SC_ISOAPPLET_ALG_REF_EC_GEN)
	{
		sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x46, 0x00, 0x00);
		apdu.data = sbuf;
		p = sbuf;
		r = isoApplet_put_ec_params(card, &args->pubkey.ec.params, p, sizeof(sbuf), &p);
		LOG_TEST_RET(card->ctx, r, "Error composing EC params.");
		apdu.datalen = p - sbuf;
		apdu.lc = p - sbuf;
		/* Use APDU chaining if the card does not support extended apdus
		 * and the data does not fit in one short apdu. */
		if ((apdu.datalen > 255) && !(card->caps & SC_CARD_CAP_APDU_EXT))
		{
			apdu.flags |= SC_APDU_FLAGS_CHAINING;
		}
	}
	else
	{
		sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x46, 0x42, 0x00);
	}

	apdu.resp = rbuf;
	apdu.resplen = sizeof(rbuf);
	apdu.le = 256;
	r = sc_transmit_apdu(card, &apdu);
	LOG_TEST_RET(card->ctx, r, "APDU transmit failed");

	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	if(apdu.sw1 == 0x6A && apdu.sw2 == 0x81)
	{
		sc_log(card->ctx, "Key generation not supported by the card with that particular key type. "
		       "Your card may not support the specified algorithm used by the applet / specified by you. "
		       "In most cases, this happens when trying to generate EC keys not supported by your java card. "
		       "In this case, look for supported field lengths and whether FP and/or F2M are supported.");
	}
	LOG_TEST_RET(card->ctx, r, "Card returned error");

	/* Parse the public key / response. */
	outer_tag_value = apdu.resp;
	r = sc_asn1_read_tag(&outer_tag_value, apdu.resplen, &cla, &tag, &outer_tag_len);
	LOG_TEST_RET(card->ctx, r, "Error in ASN1 handling.");
	/* Interindustry template for nesting one set of public key data objects */
	if((tag != 0x1F49) || (cla != 0x60))
	{
		LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA,
		             "The data returned by the card is unexpected.");
	}

	switch(args->algorithm_ref)
	{

	case SC_ISOAPPLET_ALG_REF_RSA_GEN_2048:
		/* Search for the modulus tag (81). */
		inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x81, &inner_tag_len);
		if(inner_tag_value == NULL || inner_tag_len != 256)
		{
			LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid modulus.");
		}
		if(inner_tag_len > args->pubkey.rsa.modulus.len)
		{
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL);
		}
		memcpy(args->pubkey.rsa.modulus.value, inner_tag_value, inner_tag_len);
		args->pubkey.rsa.modulus.len = inner_tag_len;

		/* Exponent tag (82) */
		inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x82, &inner_tag_len);
		if(inner_tag_value == NULL || inner_tag_len != 3)
		{
			LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid exponent.");
		}
		if(inner_tag_len > args->pubkey.rsa.exponent.len)
		{
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_BUFFER_TOO_SMALL);
		}
		if(memcmp(inner_tag_value, "\x01\x00\x01", 3) != 0)
		{
			LOG_TEST_RET(card->ctx, SC_ERROR_INCOMPATIBLE_KEY,
			             "Key generation error: Unexpected public key exponent.");
		}
		memcpy(args->pubkey.rsa.exponent.value, inner_tag_value, inner_tag_len);
		args->pubkey.rsa.exponent.len = inner_tag_len;
		p = NULL;
		break;

	case SC_ISOAPPLET_ALG_REF_EC_GEN:
		/* Compare the parameters received from the card to the ones sent to the card. */
		inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x81, &inner_tag_len);
		if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.prime.len
		        || memcmp(inner_tag_value, args->pubkey.ec.params.prime.value, inner_tag_len) != 0)
			LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid prime.");

		inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x82, &inner_tag_len);
		if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.coefficientA.len
		        || memcmp(inner_tag_value, args->pubkey.ec.params.coefficientA.value, inner_tag_len) != 0)
			LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid coefficient A.");

		inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x83, &inner_tag_len);
		if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.coefficientB.len
		        || memcmp(inner_tag_value, args->pubkey.ec.params.coefficientB.value, inner_tag_len) != 0)
			LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid coefficient B.");

		inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x84, &inner_tag_len);
		if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.basePointG.len
		        || memcmp(inner_tag_value, args->pubkey.ec.params.basePointG.value, inner_tag_len) != 0)
			LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid base point G.");

		inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x85, &inner_tag_len);
		if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.order.len
		        || memcmp(inner_tag_value, args->pubkey.ec.params.order.value, inner_tag_len) != 0)
			LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid order.");

		inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x87, &inner_tag_len);
		if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.params.coFactor.len
		        || memcmp(inner_tag_value, args->pubkey.ec.params.coFactor.value, inner_tag_len) != 0)
			LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid cofactor.");

		/* Extract public key */
		inner_tag_value = sc_asn1_find_tag(card->ctx, outer_tag_value, outer_tag_len, (unsigned int) 0x86, &inner_tag_len);
		if(inner_tag_value == NULL || inner_tag_len != args->pubkey.ec.ecPointQ.len)
			LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_DATA, "Card returned no or a invalid EC point Q.");
		memcpy(args->pubkey.ec.ecPointQ.value, inner_tag_value, inner_tag_len);

		break;
	default:
		LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unable to parse public key: Unsupported algorithm.");
	}/* switch */

	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
Пример #13
0
static int iso7816_process_fci(sc_card_t *card, sc_file_t *file,
		       const u8 *buf, size_t buflen)
{
	sc_context_t *ctx = card->ctx;
	size_t taglen, len = buflen;
	const u8 *tag = NULL, *p = buf;

	if (ctx->debug >= 3)
		sc_debug(ctx, "processing FCI bytes\n");
	tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen);
	if (tag != NULL && taglen == 2) {
		file->id = (tag[0] << 8) | tag[1];
		if (ctx->debug >= 3)
			sc_debug(ctx, "  file identifier: 0x%02X%02X\n", tag[0],
			       tag[1]);
	}
	tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen);
	if (tag != NULL && taglen >= 2) {
		int bytes = (tag[0] << 8) + tag[1];
		if (ctx->debug >= 3)
			sc_debug(ctx, "  bytes in file: %d\n", bytes);
		file->size = bytes;
	}
	if (tag == NULL) {
		tag = sc_asn1_find_tag(ctx, p, len, 0x81, &taglen);
		if (tag != NULL && taglen >= 2) {
			int bytes = (tag[0] << 8) + tag[1];
			if (ctx->debug >= 3)
				sc_debug(ctx, "  bytes in file: %d\n", bytes);
			file->size = bytes;
		}
	}
	tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen);
	if (tag != NULL) {
		if (taglen > 0) {
			unsigned char byte = tag[0];
			const char *type;

			file->shareable = byte & 0x40 ? 1 : 0;
			if (ctx->debug >= 3)
				sc_debug(ctx, "  shareable: %s\n",
				       (byte & 0x40) ? "yes" : "no");
			file->ef_structure = byte & 0x07;
			switch ((byte >> 3) & 7) {
			case 0:
				type = "working EF";
				file->type = SC_FILE_TYPE_WORKING_EF;
				break;
			case 1:
				type = "internal EF";
				file->type = SC_FILE_TYPE_INTERNAL_EF;
				break;
			case 7:
				type = "DF";
				file->type = SC_FILE_TYPE_DF;
				break;
			default:
				type = "unknown";
				break;
			}
			if (ctx->debug >= 3) {
				sc_debug(ctx, "  type: %s\n", type);
				sc_debug(ctx, "  EF structure: %d\n",
				       byte & 0x07);
			}
		}
	}
Пример #14
0
// read a DO from the card
static int gids_get_DO(sc_card_t* card, int fileIdentifier, int dataObjectIdentifier, u8* response, size_t *responselen) {
	sc_apdu_t apdu;
	int r;
	u8 data[4] = {0x5C, 0x02, (dataObjectIdentifier&0xFF00)>>8, (dataObjectIdentifier&0xFF)};
	size_t datasize = 0;
	const u8* p;
	u8 buffer[MAX_GIDS_FILE_SIZE];

	sc_format_apdu(card, &apdu,
		response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, 0xCB, (fileIdentifier&0xFF00)>>8, (fileIdentifier&0xFF));
	apdu.lc = 04;
	apdu.data = data;
	apdu.datalen = 04;
	apdu.resp = buffer;
	apdu.resplen = sizeof(buffer);
	apdu.le = 256;

	r = sc_transmit_apdu(card, &apdu);
	if (r < 0)
		return r;
	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	if (r < 0)
		return r;

	p = sc_asn1_find_tag(card->ctx, buffer, sizeof(buffer), dataObjectIdentifier, &datasize);
	if (!p) {
		return SC_ERROR_FILE_NOT_FOUND;
	}
	if (datasize > *responselen) {
		return SC_ERROR_BUFFER_TOO_SMALL;
	}
	memcpy(response, p, datasize);
	*responselen = datasize;
	return SC_SUCCESS;
}

static int print_info(sc_card_t *card) {
	int r;
	u8 buffer[MAX_GIDS_FILE_SIZE];
	size_t size = sizeof(buffer);
	u8 masterfile[MAX_GIDS_FILE_SIZE];
	size_t masterfilesize = sizeof(masterfile);
	u8 cmapfile[MAX_GIDS_FILE_SIZE];
	size_t cmapfilesize = sizeof(cmapfile);
	u8 keymap[MAX_GIDS_FILE_SIZE];
	size_t keymapsize = sizeof(keymap);
	gids_mf_record_t *records = (gids_mf_record_t *) (masterfile+1);
	int recordcount;
	int i;
	
	printf("===============================\n");
	printf("Dumping the content of the card\n");
	printf("===============================\n");
	r = gids_get_DO(card, MF_FI, MF_DO, masterfile, &masterfilesize);
	if (r < 0) {
		fprintf(stderr, "unable to retrieve the master file: %s\n", sc_strerror(r));
		fprintf(stderr, "Is that a new card ?\n");
		return r;
	}
	printf("Dumping Files:\n");
	if (masterfilesize >= 1) {
		recordcount = (int) ((masterfilesize-1) / sizeof(gids_mf_record_t));
		printf("Found %d entries in the masterfile\n", recordcount);
		for (i = 0; i < recordcount; i++) {
			if (records[i].filename[0] == 0) {
				printf("   Directory: %s\n", records[i].directory);
				printf("      FileIdentifier: 0x%x\n", records[i].fileIdentifier);
				printf("\n");
			}
		}
		for (i = 0; i < recordcount; i++) {
			if (records[i].filename[0] != 0) {
				printf("   File: %s\\%s\n", records[i].directory, records[i].filename);
				printf("      FileIdentifier: 0x%x\n", records[i].fileIdentifier);
				printf("      DataObjectIdentifier: 0x%x\n", records[i].dataObjectIdentifier);
				size = sizeof(buffer);
				r = gids_get_DO(card, records[i].fileIdentifier, records[i].dataObjectIdentifier, buffer, &size);
				if (r < 0) {
					printf("      unable to read the file: %s\n", sc_strerror(r));
				} else {
#ifdef _WIN32
					// visual studio doesn't support %zu
					printf("      Size: %Iu\n", size);
#else
					printf("      Size: %zu\n", size);
#endif
				}
				printf("\n");
				if (strcmp(records[i].directory, "mscp") == 0 && strcmp(records[i].filename, "cmapfile") == 0 ) {
					cmapfilesize = size;
					memcpy(cmapfile, buffer, size);
				}
			}
		}
		printf("Dumping containers:\n");
		if (cmapfilesize == sizeof(cmapfile)) {
			printf("Unable to find the container file (mscp\\cmapfile)\n");
		} else {
			PCONTAINER_MAP_RECORD cmaprecords = (PCONTAINER_MAP_RECORD) cmapfile;
			int cmaprecordnum = (cmapfilesize / sizeof(CONTAINER_MAP_RECORD));
			int keymaprecordnum = -1;
			struct gids_keymap_record* keymaprecord = ((struct gids_keymap_record*)(keymap +1));
			if (cmaprecordnum == 0) {
				printf("   no container found\n");
			} else {
				r = gids_get_DO(card, KEYMAP_FI, KEYMAP_DO, keymap, &keymapsize);
				if (r < 0) {
					printf("   the keymap couldn't be found\n");
				} else {
					keymaprecordnum = (keymapsize - 1) / sizeof(struct gids_keymap_record);
				}
				for (i = 0; i < cmaprecordnum; i++) {
					printf("   container:                  %d\n", i);
					wprintf(L"      guid:                    %s\n", cmaprecords[i].wszGuid);
					printf("      bFlags:                  ");
					if (cmaprecords[i].bFlags & CONTAINER_MAP_VALID_CONTAINER) {
						printf("Valid container");
						if (cmaprecords[i].bFlags & CONTAINER_MAP_DEFAULT_CONTAINER) {
							printf(",Default container");
						}
					} else {
						printf("Empty container");
					}
					printf("\n");
					printf("      wSigKeySizeBits:         %d\n", cmaprecords[i].wSigKeySizeBits);
					printf("      wKeyExchangeKeySizeBits: %d\n", cmaprecords[i].wKeyExchangeKeySizeBits);
					if (i < keymaprecordnum) {
						printf("      key info:\n");
						printf("         state:                %d\n", keymaprecord[i].state);
						printf("         algid:                %d\n", keymaprecord[i].algid);
						printf("         keyref:               0x%x\n", keymaprecord[i].keyref);
						printf("         key type:             ");
						switch(keymaprecord[i].keytype) {
						case 0:
							printf("none\n");
							break;
						case 0x9C:
							printf("signature\n");
							break;
						case 0x9A:
							printf("signature + decryption\n");
							break;
						default:
							printf("unknown\n");
							break;
						}
					}
					printf("\n");
				}
			}
		}
	} else {
		printf("No file system found\n");
	}
	return SC_SUCCESS;
}
Пример #15
0
static int sc_pkcs15emu_esteid2018_init(sc_pkcs15_card_t *p15card) {
	sc_card_t *card = p15card->card;
	u8 buff[11];
	int r, i;
	size_t field_length = 0, taglen, j;
	sc_path_t tmppath;

	set_string(&p15card->tokeninfo->label, "ID-kaart");
	set_string(&p15card->tokeninfo->manufacturer_id, "IDEMIA");

	/* Read documber number to be used as serial */
	sc_format_path("3F00D003", &tmppath);
	LOG_TEST_RET(card->ctx, sc_select_file(card, &tmppath, NULL), "SELECT docnr");
	r = sc_read_binary(card, 0, buff, 11, 0);
	LOG_TEST_RET(card->ctx, r, "read document number failed");
	const unsigned char *tag = sc_asn1_find_tag(card->ctx, buff, (size_t)r, 0x04, &taglen);
	if (tag == NULL)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);

	for (j = 0; j < taglen; j++)
		if (!isalnum(tag[j]))
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
	p15card->tokeninfo->serial_number = malloc(taglen + 1);
	if (!p15card->tokeninfo->serial_number)
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
	p15card->tokeninfo->serial_number = memcpy(p15card->tokeninfo->serial_number, tag, taglen);
	p15card->tokeninfo->serial_number[taglen] = '\0';
	p15card->tokeninfo->flags = SC_PKCS15_TOKEN_READONLY;

	/* add certificates */
	for (i = 0; i < 2; i++) {
		const char *esteid_cert_names[2] = {"Isikutuvastus", "Allkirjastamine"};
		const char *esteid_cert_paths[2] = {"3f00:adf1:3401", "3f00:adf2:341f"};
		const u8 esteid_cert_ids[2] = {1, 2};

		struct sc_pkcs15_cert_info cert_info;
		struct sc_pkcs15_object cert_obj;

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

		strlcpy(cert_obj.label, esteid_cert_names[i], sizeof(cert_obj.label));
		sc_format_path(esteid_cert_paths[i], &cert_info.path);
		cert_info.id.value[0] = esteid_cert_ids[i];
		cert_info.id.len = 1;
		r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
		if (r < 0)
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);

		// Read data from first cert
		if (i != 0)
			continue;

		sc_pkcs15_cert_t *cert = NULL;
		r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert);
		LOG_TEST_RET(card->ctx, r, "Could not read authentication certificate");

		if (cert->key->algorithm == SC_ALGORITHM_EC)
			field_length = cert->key->u.ec.params.field_length;

		const struct sc_object_id cn_oid = {{2, 5, 4, 3, -1}};
		u8 *cn_name = NULL;
		size_t cn_len = 0;
		sc_pkcs15_get_name_from_dn(card->ctx, cert->subject, cert->subject_len, &cn_oid, &cn_name, &cn_len);
		if (cn_len > 0) {
			char *token_name = malloc(cn_len + 1);
			if (token_name) {
				memcpy(token_name, cn_name, cn_len);
				token_name[cn_len] = '\0';
				set_string(&p15card->tokeninfo->label, (const char *)token_name);
				free(token_name);
			}
		}
		free(cn_name);
		sc_pkcs15_free_certificate(cert);
	}

	/* add pins */
	for (i = 0; i < 3; i++) {
		const char *esteid_pin_names[3] = {"PIN1", "PIN2", "PUK"};
		const size_t esteid_pin_min[3] = {4, 5, 8};
		const int esteid_pin_ref[3] = {0x01, 0x85, 0x02};
		const u8 esteid_pin_authid[3] = {1, 2, 3};
		const char *esteid_pin_path[3] = {"3F00", "3F00ADF2", "3F00"};

		const unsigned int esteid_pin_flags[3] = {
		    SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_INITIALIZED,
		    SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_LOCAL,
		    SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_INITIALIZED | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN};

		struct sc_pkcs15_auth_info pin_info;
		struct sc_pkcs15_object pin_obj;

		memset(&pin_info, 0, sizeof(pin_info));
		memset(&pin_obj, 0, sizeof(pin_obj));

		sc_format_path(esteid_pin_path[i], &pin_info.path);
		pin_info.auth_id.len = 1;
		pin_info.auth_id.value[0] = esteid_pin_authid[i];
		pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;
		pin_info.attrs.pin.reference = esteid_pin_ref[i];
		pin_info.attrs.pin.flags = esteid_pin_flags[i];
		pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC;
		pin_info.attrs.pin.min_length = esteid_pin_min[i];
		pin_info.attrs.pin.stored_length = 12;
		pin_info.attrs.pin.max_length = 12;
		pin_info.attrs.pin.pad_char = 0xFF;
		pin_info.tries_left = 3;
		pin_info.max_tries = 3;

		strlcpy(pin_obj.label, esteid_pin_names[i], sizeof(pin_obj.label));
		pin_obj.flags = esteid_pin_flags[i];

		/* Link normal PINs with PUK */
		if (i < 2) {
			pin_obj.auth_id.len = 1;
			pin_obj.auth_id.value[0] = 3;
		}

		r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info);
		if (r < 0)
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
	}

	// trigger PIN counter refresh via pin_cmd
	struct sc_pkcs15_object *objs[3];
	r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH, objs, 3);
	if (r != 3) {
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
	}
	for (i = 0; i < r; i++) {
		r = sc_pkcs15_get_pin_info(p15card, objs[i]);
		if (r < 0)
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
	}

	/* add private keys */
	for (i = 0; i < 2; i++) {
		const u8 prkey_pin[2] = {1, 2};

		const char *prkey_name[2] = {"Isikutuvastus", "Allkirjastamine"};
		const char *prkey_path[2] = {"3F00:ADF1", "3F00:ADF2"};
		const unsigned int prkey_usage[2] = {SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_DERIVE,
		                                     SC_PKCS15_PRKEY_USAGE_NONREPUDIATION};
		const int prkey_consent[2] = {0, 1};

		struct sc_pkcs15_prkey_info prkey_info;
		struct sc_pkcs15_object prkey_obj;

		memset(&prkey_info, 0, sizeof(prkey_info));
		memset(&prkey_obj, 0, sizeof(prkey_obj));

		sc_format_path(prkey_path[i], &prkey_info.path);
		prkey_info.id.len = 1;
		prkey_info.id.value[0] = prkey_pin[i];
		prkey_info.native = 1;
		prkey_info.key_reference = i + 1;
		prkey_info.field_length = field_length;
		prkey_info.usage = prkey_usage[i];

		strlcpy(prkey_obj.label, prkey_name[i], sizeof(prkey_obj.label));
		prkey_obj.auth_id.len = 1;
		prkey_obj.auth_id.value[0] = prkey_pin[i];
		prkey_obj.user_consent = prkey_consent[i];
		prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE;

		r = sc_pkcs15emu_add_ec_prkey(p15card, &prkey_obj, &prkey_info);
		if (r < 0)
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
	}

	return SC_SUCCESS;
}