Exemplo n.º 1
0
static int entersafe_gen_random(sc_card_t *card,u8 *buff,size_t size)
{
	 int r=SC_SUCCESS;
	 u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]={0};
	 sc_apdu_t apdu;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
   
	 sc_format_apdu(card,&apdu,SC_APDU_CASE_2_SHORT,0x84,0x00,0x00);
	 apdu.resp=rbuf;
	 apdu.le=size;
	 apdu.resplen=sizeof(rbuf);

	 r=sc_transmit_apdu(card,&apdu);
	 SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "entersafe gen random failed");

	 if(apdu.resplen!=size)
		  SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL,SC_ERROR_INTERNAL);
	 memcpy(buff,rbuf,size);

	 SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL,r);
}
Exemplo n.º 2
0
static int ias_compute_signature(sc_card_t *card, const u8 * data,
		size_t data_len, u8 * out, size_t outlen)
{
	int 			r;
	size_t 			len = 0;
	sc_apdu_t 		apdu;
	u8 				sbuf[SC_MAX_APDU_BUFFER_SIZE];
	sc_context_t 	*ctx = card->ctx;

	SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);

	if (data_len > 64) {
		sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "error: input data too long: %lu bytes\n", data_len);
		return SC_ERROR_INVALID_ARGUMENTS;
	}

	/* Send the data */
	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x88, 0x02, 0x00);
	memcpy(sbuf, data, data_len);
	apdu.data = sbuf;
	apdu.lc = data_len;
	apdu.datalen = data_len;

	r = sc_transmit_apdu(card, &apdu);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");

	/* Get the result */
	if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
		len = card->type == SC_CARD_TYPE_IAS_PTEID ? PTEID_RSA_KEYSIZE : outlen;
		r = iso_ops->get_response(card, &len, out);
		if (r == 0)
			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len);
		else
			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
	}

	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
}
Exemplo n.º 3
0
static int atrust_acos_decipher(struct sc_card *card,
			    const u8 * crgram, size_t crgram_len,
			    u8 * out, size_t outlen)
{
	int r;
	struct sc_apdu apdu;
	u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
	u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];

	assert(card != NULL && crgram != NULL && out != NULL);
	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
	if (crgram_len > 255)
		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);

	/* INS: 0x2A  PERFORM SECURITY OPERATION
	 * P1:  0x80  Resp: Plain value
	 * P2:  0x86  Cmd: Padding indicator byte followed by cryptogram */
	sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x80, 0x86);
	apdu.resp = rbuf;
	apdu.resplen = sizeof(rbuf);
	
	sbuf[0] = 0; /* padding indicator byte, 0x00 = No further indication */
	memcpy(sbuf + 1, crgram, crgram_len);
	apdu.data = sbuf;
	apdu.lc = crgram_len + 1;
	apdu.datalen = crgram_len + 1;
	apdu.le = 256;
	r = sc_transmit_apdu(card, &apdu);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
	if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
		size_t len = apdu.resplen > outlen ? outlen : apdu.resplen;

		memcpy(out, apdu.resp, len);
		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, len);
	}

	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
}
Exemplo n.º 4
0
static int
iasecc_sm_get_challenge(struct sc_card *card, unsigned char *out, size_t len)
{
	struct sc_context *ctx = card->ctx;
	struct sc_apdu apdu;
	unsigned char rbuf[SC_MAX_APDU_BUFFER_SIZE];
	int rv;

	sc_log(ctx, "SM get challenge: length %i",len);
	sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x84, 0, 0);
	apdu.le = len;
	apdu.resplen = len;
	apdu.resp = 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, "Command failed");

	memcpy(out, rbuf, apdu.resplen);

	LOG_FUNC_RETURN(ctx, apdu.resplen);
}
Exemplo n.º 5
0
static int esteid_select(struct sc_card *card, unsigned char p1, unsigned char id1, unsigned char id2) {
	struct sc_apdu apdu;
	unsigned char sbuf[2];

	LOG_FUNC_CALLED(card->ctx);

	// Select EF/DF
	sbuf[0] = id1;
	sbuf[1] = id2;

	sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xA4, p1, 0x0C);
	if (id1 != 0x3F && id2 != 0x00) {
		apdu.cse = SC_APDU_CASE_3_SHORT;
		apdu.lc = 2;
		apdu.data = sbuf;
		apdu.datalen = 2;
	}
	apdu.le = 0;
	apdu.resplen = 0;

	SC_TRANSMIT_TEST_RET(card, apdu, "SELECT failed");
	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
Exemplo n.º 6
0
static int iso7816_append_record(sc_card_t *card,
				 const u8 *buf, size_t count,
				 unsigned long flags)
{
	sc_apdu_t apdu;
	int r;

	if (count > 256) {
		sc_error(card->ctx, "Trying to send too many bytes\n");
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE2, 0, 0);
	apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3;
	
	apdu.lc = count;
	apdu.datalen = count;
	apdu.data = buf;

	r = sc_transmit_apdu(card, &apdu);
	SC_TEST_RET(card->ctx, r, "APDU transmit failed");
	SC_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2),
		    "Card returned error");
	SC_FUNC_RETURN(card->ctx, 3, count);
}
Exemplo n.º 7
0
static int
iso7816_read_record(struct sc_card *card,
		unsigned int rec_nr, u8 *buf, size_t count, unsigned long flags)
{
	struct sc_apdu apdu;
	int r;

	sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0xB2, rec_nr, 0);
	apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3;
	if (flags & SC_RECORD_BY_REC_NR)
		apdu.p2 |= 0x04;

	apdu.le = count;
	apdu.resplen = count;
	apdu.resp = buf;

	fixup_transceive_length(card, &apdu);
	r = sc_transmit_apdu(card, &apdu);
	LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
	if (apdu.resplen == 0)
		LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2));

	LOG_FUNC_RETURN(card->ctx, apdu.resplen);
}
Exemplo n.º 8
0
static int atrust_acos_logout(struct sc_card *card)
{
	int r;
	struct sc_apdu apdu;
	const u8 mf_buf[2] = {0x3f, 0x00};

	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x00, 0x0C);
	apdu.le = 0;
	apdu.lc = 2;
	apdu.data    = mf_buf;
	apdu.datalen = 2;
	apdu.resplen = 0;
	
	r = sc_transmit_apdu(card, &apdu);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU re-transmit failed");

	if (apdu.sw1 == 0x69 && apdu.sw2 == 0x85)
		/* the only possible reason for this error here is, afaik,
		 * that no MF exists, but then there's no need to logout
		 * => return SC_SUCCESS
		 */
		return SC_SUCCESS;
	return sc_check_sw(card, apdu.sw1, apdu.sw2);
}
Exemplo n.º 9
0
static int
iso7816_update_record(struct sc_card *card, unsigned int rec_nr,
		const u8 *buf, size_t count, unsigned long flags)
{
	struct sc_apdu apdu;
	int r;

	sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDC, rec_nr, 0);
	apdu.p2 = (flags & SC_RECORD_EF_ID_MASK) << 3;
	if (flags & SC_RECORD_BY_REC_NR)
		apdu.p2 |= 0x04;

	apdu.lc = count;
	apdu.datalen = count;
	apdu.data = buf;

	fixup_transceive_length(card, &apdu);
	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");

	LOG_FUNC_RETURN(card->ctx, count);
}
Exemplo n.º 10
0
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);
}
Exemplo n.º 11
0
static int
jpki_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
{
	int rc;
	sc_path_t path;
	sc_apdu_t apdu;
	struct jpki_private_data *priv = JPKI_DRVDATA(card);
	int max_tries = 0;

	LOG_FUNC_CALLED(card->ctx);

	if (tries_left) {
		*tries_left = -1;
	}

	switch (data->pin_reference) {
	case 1:
		sc_format_path(JPKI_AUTH_PIN, &path);
		path.type = SC_PATH_TYPE_FILE_ID;
		rc = sc_select_file(card, &path, NULL);
		max_tries = JPKI_AUTH_PIN_MAX_TRIES;
		break;
	case 2:
		sc_format_path(JPKI_SIGN_PIN, &path);
		path.type = SC_PATH_TYPE_FILE_ID;
		rc = sc_select_file(card, &path, NULL);
		max_tries = JPKI_SIGN_PIN_MAX_TRIES;
		break;
	default:
		sc_log(card->ctx, "Unknown PIN reference: %d", data->pin_reference);
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
	}
	LOG_TEST_RET(card->ctx, rc, "SELECT_FILE error");

	switch (data->cmd) {
	case SC_PIN_CMD_VERIFY:
		sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0x20, 0x00, 0x80);
		apdu.data = data->pin1.data;
		apdu.datalen = data->pin1.len;
		apdu.lc = data->pin1.len;
		rc = sc_transmit_apdu(card, &apdu);
		LOG_TEST_RET(card->ctx, rc, "APDU transmit failed");
		rc = sc_check_sw(card, apdu.sw1, apdu.sw2);
		if (rc == SC_SUCCESS) {
			data->pin1.logged_in = SC_PIN_STATE_LOGGED_IN;
			data->pin1.tries_left = max_tries;
		} else {
			data->pin1.logged_in = SC_PIN_STATE_LOGGED_OUT;
			data->pin1.tries_left = apdu.sw2 & 0xF;
		}
		priv->logged_in = data->pin1.logged_in;
		LOG_TEST_RET(card->ctx, rc, "VERIFY failed");
		break;
	case SC_PIN_CMD_GET_INFO:
		sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, 0x80);
		rc = sc_transmit_apdu(card, &apdu);
		LOG_TEST_RET(card->ctx, rc, "APDU transmit failed");
		if (apdu.sw1 != 0x63) {
			sc_log(card->ctx, "VERIFY GET_INFO error");
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_CARD_CMD_FAILED);
		}
		data->pin1.logged_in = priv->logged_in;
		data->pin1.tries_left = apdu.sw2 & 0xF;
		if (tries_left) {
			*tries_left = data->pin1.tries_left;
		}
		break;
	default:
		sc_log(card->ctx, "Card does not support PIN command: %d", data->cmd);
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
	}

	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
Exemplo n.º 12
0
static int
jpki_select_file(struct sc_card *card,
		 const struct sc_path *path, struct sc_file **file_out)
{
	struct jpki_private_data *drvdata = JPKI_DRVDATA(card);
	int rc;
	sc_apdu_t apdu;
	struct sc_file *file = NULL;

	LOG_FUNC_CALLED(card->ctx);
	sc_log(card->ctx,
	       "jpki_select_file: path=%s, len=%"SC_FORMAT_LEN_SIZE_T"u",
	       sc_print_path(path), path->len);
	if (path->len == 2 && memcmp(path->value, "\x3F\x00", 2) == 0) {
		drvdata->selected = SELECT_MF;
		if (file_out) {
			sc_file_dup(file_out, drvdata->mf);
			if (*file_out == NULL) {
				LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
			}
		}
		return 0;
	}

	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0, 0);
	switch (path->type) {
	case SC_PATH_TYPE_FILE_ID:
		apdu.p1 = 2;
		break;
	case SC_PATH_TYPE_DF_NAME:
		apdu.p1 = 4;
		break;
	default:
		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
	}
	apdu.p2 = 0x0C;
	apdu.data = path->value;
	apdu.datalen = path->len;
	apdu.lc = path->len;

	rc = sc_transmit_apdu(card, &apdu);
	LOG_TEST_RET(card->ctx, rc, "APDU transmit failed");
	rc = sc_check_sw(card, apdu.sw1, apdu.sw2);
	LOG_TEST_RET(card->ctx, rc, "SW Check failed");
	if (!file_out) {
		LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
	}

	/* read certificate file size */
	if (path->len == 2 && (
		    memcmp(path->value, "\x00\x0A", 2) == 0 ||
		    memcmp(path->value, "\x00\x01", 2) == 0 ||
		    memcmp(path->value, "\x00\x0B", 2) == 0 ||
		    memcmp(path->value, "\x00\x02", 2) == 0 )
		) {
		u8 buf[4];
		rc = sc_read_binary(card, 0, buf, 4, 0);
		LOG_TEST_RET(card->ctx, rc, "SW Check failed");
		file = sc_file_new();
		if (!file) {
			LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
		}
		file->path = *path;
		file->size = (buf[2] << 8 | buf[3]) + 4;
		*file_out = file;
	}
	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
Exemplo n.º 13
0
static int ias_select_file(sc_card_t *card, const sc_path_t *in_path,
		sc_file_t **file_out)
{
	int 			r, pathlen, stripped_len;
	u8 				buf[SC_MAX_APDU_BUFFER_SIZE];
	u8 				pathbuf[SC_MAX_PATH_SIZE], *path;
	sc_apdu_t 		apdu;
	sc_file_t 		*file;

	stripped_len = 0;
	path = pathbuf;
	file = NULL;

	assert(card != NULL && in_path != NULL);

	if (in_path->len > SC_MAX_PATH_SIZE)
		return SC_ERROR_INVALID_ARGUMENTS;
	memcpy(path, in_path->value, in_path->len);
	pathlen = in_path->len;

	sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0);
	apdu.p2 = 0; /* First record, return FCI */

	switch (in_path->type) {
	case SC_PATH_TYPE_FILE_ID:
		apdu.p1 = 2;
		if (pathlen != 2)
			return SC_ERROR_INVALID_ARGUMENTS;
		break;
	case SC_PATH_TYPE_DF_NAME:
		apdu.p1 = 4;
		break;
	case SC_PATH_TYPE_PATH:
		apdu.p1 = 9;
		/* Strip the MF */
		if (pathlen >= 2 && memcmp(path, "\x3f\x00", 2) == 0) {
			if (pathlen == 2) { /* Only 3f00 provided */
				apdu.p1 = 0;
				break;
			}
			path += 2;
			pathlen -= 2;
		}
		/* Optimization based on the normal Portuguese eID usage pattern:
		 * paths with len >= 4 shall be stripped - this avoids unnecessary
		 * "file not found" errors. Other cards may benefit from this also.
		 *
		 * This works perfectly for the Portuguese eID card, but if you
		 * are adapting this driver to another card, "false positives" may
		 * occur depending, of course, on the file structure of the card.
		 *
		 * Please have this in mind if adapting this driver to another card.
		 */
		if (pathlen >= 4) {
			stripped_len = pathlen - 2;
			path += stripped_len;
			pathlen = 2;
		} else if (pathlen == 2) {
			apdu.p1 = 0;
		}
		break;
	case SC_PATH_TYPE_FROM_CURRENT:
		apdu.p1 = 9;
		break;
	case SC_PATH_TYPE_PARENT:
		apdu.p1 = 3;
		apdu.p2 = 0x0C;
		pathlen = 0;
		apdu.cse = SC_APDU_CASE_2_SHORT;
		break;
	default:
		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
	}

	apdu.lc = pathlen;
	apdu.data = path;
	apdu.datalen = pathlen;

	if (file_out != NULL) {
		apdu.resp = buf;
		apdu.resplen = sizeof(buf);
		apdu.le = 256;
	} else {
		apdu.p2 = 0x0C;
		apdu.cse = (apdu.lc == 0) ? SC_APDU_CASE_1 : SC_APDU_CASE_3_SHORT;
	}

	r = sc_transmit_apdu(card, &apdu);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
	if (file_out == NULL) {
		if (apdu.sw1 == 0x61)
			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 0);
		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
	}

	/* A "file not found" error was received, this can mean two things:
	 * 1) the file does not exist
	 * 2) the current DF may be incorrect due to the optimization applied
	 *    earlier. If the path was previously stripped, select the first DF
	 *    and try to re-select the path with the full value.
	 */
	if (stripped_len > 0 && apdu.sw1 == 0x6A && apdu.sw2 == 0x82) {
		sc_path_t tpath;

		/* Restore original path value */
		path -= stripped_len;
		pathlen += stripped_len;

		memset(&tpath, 0, sizeof(sc_path_t));
		tpath.type = SC_PATH_TYPE_PATH;
		tpath.len = 2;
		tpath.value[0] = path[0];
		tpath.value[1] = path[1];

		/* Go up in the hierarchy to the correct DF */
		r = ias_select_file(card, &tpath, NULL);
		SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Error selecting parent.");

		/* We're now in the right place, reconstruct the APDU and retry */
		path += 2;
		pathlen -= 2;
		apdu.lc = pathlen;
		apdu.data = path;
		apdu.datalen = pathlen;

		if (file_out != NULL)
			apdu.resplen = sizeof(buf);

		r = sc_transmit_apdu(card, &apdu);
		SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
		if (file_out == NULL) {
			if (apdu.sw1 == 0x61)
				SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 0);
			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
		}
	}

	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	if (r)
		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);

	if (apdu.resplen < 2)
		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED);
	switch (apdu.resp[0]) {
	case 0x6F:
		file = sc_file_new();
		if (file == NULL)
			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY);
		file->path = *in_path;
		if (card->ops->process_fci == NULL) {
			sc_file_free(file);
			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED);
		}
		if ((size_t)apdu.resp[1] + 2 <= apdu.resplen)
			card->ops->process_fci(card, file, apdu.resp+2, apdu.resp[1]);
		*file_out = file;
		break;
	case 0x00:	/* proprietary coding */
		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED);
	default:
		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED);
	}

	return SC_SUCCESS;
}
Exemplo n.º 14
0
static int sc_pkcs15emu_gemsafeV1_init( sc_pkcs15_card_t *p15card)
{
	int		    r;
	unsigned int    i;
	struct sc_path  path;
	struct sc_file *file = NULL;
	struct sc_card *card = p15card->card;
	struct sc_apdu  apdu;
	u8		    rbuf[SC_MAX_APDU_BUFFER_SIZE];

	sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Setting pkcs15 parameters\n");

	if (p15card->tokeninfo->label)
		free(p15card->tokeninfo->label);
	p15card->tokeninfo->label = malloc(strlen(APPLET_NAME) + 1);
	if (!p15card->tokeninfo->label)
		return SC_ERROR_INTERNAL;
	strcpy(p15card->tokeninfo->label, APPLET_NAME);

	if (p15card->tokeninfo->serial_number)
		free(p15card->tokeninfo->serial_number);
	p15card->tokeninfo->serial_number = malloc(strlen(DRIVER_SERIAL_NUMBER) + 1);
	if (!p15card->tokeninfo->serial_number)
		return SC_ERROR_INTERNAL;
	strcpy(p15card->tokeninfo->serial_number, DRIVER_SERIAL_NUMBER);

	/* the GemSAFE applet version number */
	sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0xdf, 0x03);
	apdu.cla = 0x80;
	apdu.resp = rbuf;
	apdu.resplen = sizeof(rbuf);
	/* Manual says Le=0x05, but should be 0x08 to return full version numer */
	apdu.le = 0x08;
	apdu.lc = 0;
	apdu.datalen = 0;
	r = sc_transmit_apdu(card, &apdu);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
	if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
		return SC_ERROR_INTERNAL;
	if (r != SC_SUCCESS)
		return SC_ERROR_INTERNAL;

	/* the manufacturer ID, in this case GemPlus */
	if (p15card->tokeninfo->manufacturer_id)
		free(p15card->tokeninfo->manufacturer_id);
	p15card->tokeninfo->manufacturer_id = malloc(strlen(MANU_ID) + 1);
	if (!p15card->tokeninfo->manufacturer_id)
		return SC_ERROR_INTERNAL;
	strcpy(p15card->tokeninfo->manufacturer_id, MANU_ID);

	/* determine allocated key containers and length of certificates */
	r = gemsafe_get_cert_len(card);
	if (r != SC_SUCCESS)
		return SC_ERROR_INTERNAL;

	/* set certs */
	sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Setting certificates\n");
	for (i = 0; i < gemsafe_cert_max; i++) {
		struct sc_pkcs15_id p15Id;
		struct sc_path path;

		if (gemsafe_cert[i].label == NULL)
			continue;
		sc_format_path(gemsafe_cert[i].path, &path);
		sc_pkcs15_format_id(gemsafe_cert[i].id, &p15Id);
		path.index = gemsafe_cert[i].index;
		path.count = gemsafe_cert[i].count;
		sc_pkcs15emu_add_cert(p15card, SC_PKCS15_TYPE_CERT_X509,
				      gemsafe_cert[i].authority, &path, &p15Id,
				      gemsafe_cert[i].label, gemsafe_cert[i].obj_flags);
	}

	/* set gemsafe_pin */
	sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Setting PIN\n");
	for (i=0; i < gemsafe_pin_max; i++) {
		struct sc_pkcs15_id	p15Id;
		struct sc_path path;

		sc_pkcs15_format_id(gemsafe_pin[i].id, &p15Id);
		sc_format_path(gemsafe_pin[i].path, &path);
		if (gemsafe_pin[i].atr_len == 0 ||
		   (gemsafe_pin[i].atr_len == p15card->card->atr.len &&
		    memcmp(p15card->card->atr.value, gemsafe_pin[i].atr,
			   p15card->card->atr.len) == 0)) {
			sc_pkcs15emu_add_pin(p15card, &p15Id, gemsafe_pin[i].label,
					     &path, gemsafe_pin[i].ref, gemsafe_pin[i].type,
					     gemsafe_pin[i].minlen, gemsafe_pin[i].maxlen,
					     gemsafe_pin[i].flags, gemsafe_pin[i].tries_left,
					     gemsafe_pin[i].pad_char, gemsafe_pin[i].obj_flags);
			break;
		}
	};

	/* set private keys */
	sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL, "Setting private keys\n");
	for (i = 0; i < gemsafe_cert_max; i++) {
		struct sc_pkcs15_id p15Id, authId, *pauthId;
		struct sc_path path;
		int key_ref = 0x03; 

		if (gemsafe_prkeys[i].label == NULL)
			continue;
		sc_pkcs15_format_id(gemsafe_prkeys[i].id, &p15Id);
		if (gemsafe_prkeys[i].auth_id) {
			sc_pkcs15_format_id(gemsafe_prkeys[i].auth_id, &authId);
			pauthId = &authId;
		} else
			pauthId = NULL;
		sc_format_path(gemsafe_prkeys[i].path, &path);
		/* 
		 * The key ref may be different for different sites;
		 * by adding flags=n where the low order 4 bits can be
		 * the key ref we can force it.
		 */
		if ( p15card->card->flags & 0x0F) {
			key_ref = p15card->card->flags & 0x0F;
			sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL,
				 "Overriding key_ref %d with %d\n",
				 gemsafe_prkeys[i].ref, key_ref);
		} else
			key_ref = gemsafe_prkeys[i].ref;
		sc_pkcs15emu_add_prkey(p15card, &p15Id, gemsafe_prkeys[i].label,
				       SC_PKCS15_TYPE_PRKEY_RSA,
				       gemsafe_prkeys[i].modulus_len, gemsafe_prkeys[i].usage,
				       &path, key_ref, pauthId,
				       gemsafe_prkeys[i].obj_flags);
	}

	/* select the application DF */
	sc_debug(p15card->card->ctx, SC_LOG_DEBUG_NORMAL,"Selecting application DF\n");
	sc_format_path(GEMSAFE_APP_PATH, &path);
	r = sc_select_file(card, &path, &file);
	if (r != SC_SUCCESS || !file)
		return SC_ERROR_INTERNAL;
	/* set the application DF */
	if (p15card->file_app)
		free(p15card->file_app);
	p15card->file_app = file;

	return SC_SUCCESS;
}
Exemplo n.º 15
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);
}
Exemplo n.º 16
0
static int get_language(sc_card_t *card)
{
	sc_apdu_t apdu;
	u8 prefs[240], *lg_value;
	u8 path[] = { 0x3F, 0x00, 0xDF, 0x01, 0x40, 0x39 };
	int r, i, len;

	/* Get the language from the card's preferences file */
	assert(card != NULL);

	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 0x08, 0x0C);
	apdu.lc = sizeof(path);
	apdu.data = path;
	apdu.datalen = sizeof(path);
	apdu.resplen = 0;
	apdu.le = 0;

	r = sc_lock(card);
	if (r < 0)
		goto prefs_error;

	r = sc_transmit_apdu(card, &apdu);
	if (r < 0) {
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Select_File[prefs_file] command failed: %d\n", r);
		sc_unlock(card);
		goto prefs_error;
	}
	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	if (r < 0) {
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Select_File[prefs_file]: card returned %d\n", r);
		sc_unlock(card);
		goto prefs_error;
	}

	r = iso_ops->read_binary(card, 0, prefs, sizeof(prefs), 0);
	sc_unlock(card);
	if (r <= 0) {
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Read_Binary[prefs_file] returned %d\n", r);
		goto prefs_error;
	}
#if 0
	dumphex("Prefs: ", prefs, r);
#endif
	i = get_pref(prefs, r, "[gen]", "lg", &len);
	if (i <= 0 || len < 2) {
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Couldn't find language in prefs file: %d\n", i);
		goto prefs_error;
	}
	lg_value = prefs + i;	/* language code(s) found, starts here */
	i = 0;
	while (1) {
		while (i <= len - 2 && (lg_value[i] == ' ' || lg_value[i] == '|'))
			i++;
		if (i > len - 2)
			goto prefs_error;
		r = str2lang(card->ctx, lg_value + i);
		if (r >= 0)
			return r;
		i += 2;
	}

      prefs_error:
	/* If troubles with the card's prefs file, get the language from the OS */
#ifdef _WIN32
	switch (GetUserDefaultLangID() & 0x00FF) {
	case 0x13:
		return LNG_DUTCH;
	case 0x0C:
		return LNG_FRENCH;
	case 0x07:
		return LNG_GERMAN;
	default:
		return LNG_ENG;
	}
#endif
	return LNG_ENG;		/* default */
}
Exemplo n.º 17
0
static int mcrd_init(sc_card_t * card)
{
	unsigned long flags;
	struct mcrd_priv_data *priv;
	int r;
	sc_path_t tmppath;
	sc_apdu_t apdu;

	priv = calloc(1, sizeof *priv);
	if (!priv)
		return SC_ERROR_OUT_OF_MEMORY;
	card->drv_data = priv;
	card->cla = 0x00;
	card->caps = SC_CARD_CAP_RNG;


	if (is_esteid_card(card)) {
		/* Reset the MULTOS card to get to a known state */
		if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V11)
			sc_reset(card, 0);

		/* Select the EstEID AID to get to a known state.
		 * For some reason a reset is required as well... */
		if (card->type == SC_CARD_TYPE_MCRD_ESTEID_V30) {
			flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_HASH_SHA1 | SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA256;
			/* EstEID v3.0 has 2048 bit keys */
			_sc_card_add_rsa_alg(card, 2048, flags, 0);
			sc_reset(card, 0);

			sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xA4, 0x04, 0x00);
			apdu.lc = sizeof(EstEID_v3_AID);
			apdu.data = EstEID_v3_AID;
			apdu.datalen = sizeof(EstEID_v3_AID);
			apdu.resplen = 0;
			apdu.le = 0;
			r = sc_transmit_apdu(card, &apdu);
			SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "SELECT AID: %02X%02X", apdu.sw1, apdu.sw2);
			if(apdu.sw1 != 0x90 && apdu.sw2 != 0x00)
			{
				sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xA4, 0x04, 0x00);
	                        apdu.lc = sizeof(EstEID_v35_AID);
        	                apdu.data = EstEID_v35_AID;
                	        apdu.datalen = sizeof(EstEID_v35_AID);
                        	apdu.resplen = 0;
	                        apdu.le = 0;
				r = sc_transmit_apdu(card, &apdu);
	                        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
        	                sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "SELECT AID: %02X%02X", apdu.sw1, apdu.sw2);
				if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) {
					sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xA4, 0x04, 0x00);
					apdu.lc = sizeof(AzeDIT_v35_AID);
					apdu.data = AzeDIT_v35_AID;
					apdu.datalen = sizeof(AzeDIT_v35_AID);
					apdu.resplen = 0;
					apdu.le = 0;
					r = sc_transmit_apdu(card, &apdu);
					SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
					sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "SELECT AID: %02X%02X", apdu.sw1, apdu.sw2);
					if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00)
						SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,  SC_ERROR_CARD_CMD_FAILED);
				}
			}
		} else {
			/* EstEID v1.0 and 1.1 have 1024 bit keys */
			flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_SHA1;
			_sc_card_add_rsa_alg(card, 1024, flags, 0);
		}
	} else {
		flags = SC_ALGORITHM_RSA_RAW |SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE;
		_sc_card_add_rsa_alg(card, 512, flags, 0);
		_sc_card_add_rsa_alg(card, 768, flags, 0);
		_sc_card_add_rsa_alg(card, 1024, flags, 0);
	}

	priv->curpath[0] = MFID;
	priv->curpathlen = 1;

	sc_format_path ("3f00", &tmppath);
	r = sc_select_file (card, &tmppath, NULL);

	/* Not needed for the fixed EstEID profile */
	if (!is_esteid_card(card))
		load_special_files(card);

	return r;
}
/** atrust_acos_set_security_env
 * sets the security enviroment
 * \param card pointer to the sc_card object
 * \param env pointer to a sc_security_env object
 * \param se_num not used here
 * \return SC_SUCCESS on success or an error code
 *
 * This function sets the security enviroment (using the 
 * command MANAGE SECURITY ENVIROMENT). In case a COMPUTE SIGNATURE
 * operation is requested , this function tries to detect whether
 * COMPUTE SIGNATURE or INTERNAL AUTHENTICATE must be used for signature
 * calculation.
 */
static int atrust_acos_set_security_env(struct sc_card *card,
				    const struct sc_security_env *env,
				    int se_num)
{
	u8              *p, *pp, keyID;
	int              r, operation = env->operation;
	struct sc_apdu   apdu;
	u8               sbuf[SC_MAX_APDU_BUFFER_SIZE];
	atrust_acos_ex_data *ex_data = (atrust_acos_ex_data *)card->drv_data;

	p     = sbuf;
	keyID = env->key_ref[0];

	/* copy key reference, if present */
	if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT) {
		if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC)
			*p++ = 0x83;
		else
			*p++ = 0x84;
		*p++ = env->key_ref_len;
		memcpy(p, env->key_ref, env->key_ref_len);
		p += env->key_ref_len;
	}
	pp = p;
	if (operation == SC_SEC_OPERATION_DECIPHER){
		if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) {
			*p++ = 0x80;
			*p++ = 0x01;
			*p++ = 0x02;
		} else
			return SC_ERROR_INVALID_ARGUMENTS;
		sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x81,
		               0xb8);
		apdu.data    = sbuf;
		apdu.datalen = p - sbuf;
		apdu.lc      = p - sbuf;
		apdu.le      = 0;
		r = sc_transmit_apdu(card, &apdu);
		SC_TEST_RET(card->ctx, r, "APDU transmit failed");
		if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
			SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2));
		return SC_SUCCESS;
	}
	/* try COMPUTE SIGNATURE */
	if (operation == SC_SEC_OPERATION_SIGN && (
	    env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1 ||
	    env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796)) {
		if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) {
			*p++ = 0x80;
			*p++ = 0x01;
			*p++ = env->algorithm_ref & 0xFF;
		} else if (env->flags & SC_SEC_ENV_ALG_PRESENT &&
		            env->algorithm == SC_ALGORITHM_RSA) {
			/* set the method to use based on the algorithm_flags */
			*p++ = 0x80;
			*p++ = 0x01;
			if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) {
				if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1)
					*p++ = 0x12;
				else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_RIPEMD160)
					*p++ = 0x22;
				else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_MD5)
					*p++ = 0x32;
				else {
					/* can't use COMPUTE SIGNATURE =>
					 * try INTERNAL AUTHENTICATE */
					p = pp;
					operation = SC_SEC_OPERATION_AUTHENTICATE;
					goto try_authenticate;
				}
			} else if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796) {
				if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1)
					*p++ = 0x11;
				else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_RIPEMD160)
					*p++ = 0x21;
				else
					return SC_ERROR_INVALID_ARGUMENTS;
			} else
				return SC_ERROR_INVALID_ARGUMENTS;
		}
		sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xb6);
		apdu.data    = sbuf;
		apdu.datalen = p - sbuf;
		apdu.lc      = p - sbuf;
		apdu.le      = 0;
		/* suppress errors, as don't know whether to use 
		 * COMPUTE SIGNATURE or INTERNAL AUTHENTICATE */
		sc_ctx_suppress_errors_on(card->ctx);
		r = sc_transmit_apdu(card, &apdu);
		sc_ctx_suppress_errors_off(card->ctx);
		SC_TEST_RET(card->ctx, r, "APDU transmit failed");
		if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
			ex_data->fix_digestInfo = 0;
			ex_data->sec_ops        = SC_SEC_OPERATION_SIGN;
			return SC_SUCCESS;
		}
		/* reset pointer */
		p = pp;
		/* doesn't work => try next op */
		operation = SC_SEC_OPERATION_AUTHENTICATE;
	}
try_authenticate:
	/* try INTERNAL AUTHENTICATE */
	if (operation == SC_SEC_OPERATION_AUTHENTICATE && 
	    env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) {
		*p++ = 0x80;
		*p++ = 0x01;
		*p++ = 0x01;
		
		sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0xa4);
		apdu.data    = sbuf;
		apdu.datalen = p - sbuf;
		apdu.lc      = p - sbuf;
		apdu.le      = 0;
		r = sc_transmit_apdu(card, &apdu);
		SC_TEST_RET(card->ctx, r, "APDU transmit failed");
		if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
			SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2));
		ex_data->fix_digestInfo = env->algorithm_flags;
		ex_data->sec_ops        = SC_SEC_OPERATION_AUTHENTICATE;
		return SC_SUCCESS;
	}

	return SC_ERROR_INVALID_ARGUMENTS;
}
Exemplo n.º 19
0
/*
 * @brief Import a private key.
 */
static int
isoApplet_ctl_import_key(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args)
{
	int r;
	sc_apdu_t apdu;
	u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
	u8 *p;

	LOG_FUNC_CALLED(card->ctx);

	/*
	 * Private keys are not stored in the filesystem.
	 * ISO 7816-8 - section C.2	 describes:
	 * "Usage of the PUT DATA command for private key import"
	 * The applet uses this PUT DATA to import private keys, if private key import is allowed.
	 *
	 * The first step is to perform a MANAGE SECURITY ENVIRONMENT as it would be done
	 * with on-card key generation. The second step is PUT DATA (instead of
	 * GENERATE ASYMMETRIC KEYPAIR).
	 */

	/* 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, "%s: APDU transmit failed");

	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	LOG_TEST_RET(card->ctx, r, "Card returned error");


	/* PUT DATA */
	switch(args->algorithm_ref)
	{

	case SC_ISOAPPLET_ALG_REF_RSA_GEN_2048:
		r = isoApplet_put_data_prkey_rsa(card, args);
		LOG_TEST_RET(card->ctx, r, "Error in PUT DATA.");
		break;

	case SC_ISOAPPLET_ALG_REF_EC_GEN:
		r = isoApplet_put_data_prkey_ec(card, args);
		LOG_TEST_RET(card->ctx, r, "Error in PUT DATA.");
		break;

	default:
		LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Uknown algorithm refernce.");
	}

	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}
Exemplo n.º 20
0
/*
 * @brief Use PUT DATA to import a private EC key.
 *
 * Format of transmitted data:
 *  0xE0 - Private class, constructed encoding, number one.
 *  0x81 - prime
 *  0x82 - coefficient A
 *  0x83 - coefficient B
 *  0x84 - base point G
 *  0x85 - order
 *  0x87 - cofactor
 *  0x88 - private D (private key)
 *
 * @param card
 * @param ec   The EC private key to import.
 *
 * @return SC_ERROR_INVALID_ARGUMENTS: Curve parameters or private component is missing.
 *         other errors:               Transmit errors / errors returned by card.
 *                                     ASN1 errors.
 */
static int
isoApplet_put_data_prkey_ec(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args)
{
	sc_apdu_t apdu;
	u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
	int r;
	u8 *p;
	size_t tags_len;

	LOG_FUNC_CALLED(card->ctx);

	if(!args->privkey.ec.privateD.value
	        || !args->privkey.ec.params.prime.value
	        || !args->privkey.ec.params.coefficientA.value
	        || !args->privkey.ec.params.coefficientB.value
	        || !args->privkey.ec.params.basePointG.value
	        || !args->privkey.ec.params.order.value
	        || !args->privkey.ec.params.coFactor.value
	  )
	{
		LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "Missing information about EC private key.");
	}

	/* Calculate the length of all inner tag-length-value entries, but do not write anything yet. */
	tags_len = 0;
	r = sc_asn1_put_tag(0x81, NULL, args->privkey.ec.params.prime.len, NULL, 0, NULL);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
	tags_len += r;
	r = sc_asn1_put_tag(0x82, NULL, args->privkey.ec.params.coefficientA.len, NULL, 0, NULL);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
	tags_len += r;
	r = sc_asn1_put_tag(0x83, NULL, args->privkey.ec.params.coefficientB.len, NULL, 0, NULL);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
	tags_len += r;
	r = sc_asn1_put_tag(0x84, NULL, args->privkey.ec.params.basePointG.len, NULL, 0, NULL);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
	tags_len += r;
	r = sc_asn1_put_tag(0x85, NULL, args->privkey.ec.params.order.len, NULL, 0, NULL);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
	tags_len += r;
	r = sc_asn1_put_tag(0x87, NULL, args->privkey.ec.params.coFactor.len, NULL, 0, NULL);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
	tags_len += r;
	r = sc_asn1_put_tag(0x88, NULL, args->privkey.ec.privateD.len, NULL, 0, NULL);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
	tags_len += r;

	/* Write the outer tag and length information. */
	p = sbuf;
	r = sc_asn1_put_tag(0xE0, NULL, tags_len, p, sizeof(sbuf), &p);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");

	/* Write inner tags. */
	r = isoApplet_put_ec_params(card, &args->privkey.ec.params, p, sizeof(sbuf) - (p - sbuf), &p);
	if(r < 0)
	{
		sc_log(card->ctx, "Error composing EC params.");
		goto out;
	}
	r = sc_asn1_put_tag(0x88, args->privkey.ec.privateD.value, args->privkey.ec.privateD.len, p, sizeof(sbuf) - (p - sbuf), &p);
	if(r < 0)
		goto out;

	/* Send to card. */
	sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDB, 0x3F, 0xFF);
	apdu.lc = p - sbuf;
	apdu.datalen = p - sbuf;
	apdu.data = sbuf;
	r = sc_transmit_apdu(card, &apdu);
	if(r < 0)
	{
		sc_log(card->ctx, "APDU transmit failed");
		goto out;
	}

	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	if(apdu.sw1 == 0x6D && apdu.sw2 == 0x00)
	{
		sc_log(card->ctx, "The applet returned that the PUT DATA instruction byte is not supported. "
		       "If you are using an older applet version and are trying to import keys, please update your applet first.");
	}
	else if(apdu.sw1 == 0x6A && apdu.sw2 == 0x81)
	{
		sc_log(card->ctx, "Key import 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 import EC keys not supported by your java card. "
		       "In this case, look for supported field lengths and whether FP and/or F2M are supported. "
		       "If you tried to import a private RSA key, check the key length.");
	}
	else if(apdu.sw1 == 0x69 && apdu.sw2 == 0x00)
	{
		sc_log(card->ctx, "Key import not allowed by the applet's security policy. "
		       "If you want to allow key import, set DEF_PRIVATE_KEY_IMPORT_ALLOWED in the IsoApplet,"
		       " rebuild and reinstall the applet.");
	}
	if(r < 0)
	{
		sc_log(card->ctx, "Card returned error");
		goto out;
	}

	r = SC_SUCCESS;
out:
	sc_mem_clear(sbuf, sizeof(sbuf));
	LOG_FUNC_RETURN(card->ctx, r);
}
Exemplo n.º 21
0
/*
 * @brief Use PUT DATA to import a private RSA key.
 *
 * For simplicity, command chaining has to be used. One chunk (apdu) must contain
 * one RSA field (P, Q, etc.). The first apdu must contain the outer tag (7F48).
 *
 * @param card
 * @param rsa The RSA private key to import.
 *
 * @return SC_ERROR_INVALID_ARGUMENTS: The RSA key does not contain CRT fields.
 *		   other errors:               Transmit errors / errors returned by card.
 */
static int
isoApplet_put_data_prkey_rsa(sc_card_t *card, sc_cardctl_isoApplet_import_key_t *args)
{
	sc_apdu_t apdu;
	u8 sbuf[SC_MAX_EXT_APDU_BUFFER_SIZE];
	u8 *p = NULL;
	int r;
	size_t tags_len;

	LOG_FUNC_CALLED(card->ctx);

	if(!args->privkey.rsa.p.value
	        || !args->privkey.rsa.q.value
	        || !args->privkey.rsa.iqmp.value
	        || !args->privkey.rsa.dmp1.value
	        || !args->privkey.rsa.dmq1.value)
	{
		LOG_TEST_RET(card->ctx, SC_ERROR_INVALID_ARGUMENTS, "RSA key is missing information.");
	}

	/* Note: The format is according to ISO 2-byte tag 7F48
	 * "T-L pair to indicate a private key data object" */

	/* Calculate the length of all inner tag-length-value entries, but do not write anything yet. */
	tags_len = 0;
	r = sc_asn1_put_tag(0x92, NULL, args->privkey.rsa.p.len, NULL, 0, NULL);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
	tags_len += r;
	r = sc_asn1_put_tag(0x93, NULL, args->privkey.rsa.q.len, NULL, 0, NULL);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
	tags_len += r;
	r = sc_asn1_put_tag(0x94, NULL, args->privkey.rsa.iqmp.len, NULL, 0, NULL);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
	tags_len += r;
	r = sc_asn1_put_tag(0x95, NULL, args->privkey.rsa.dmp1.len, NULL, 0, NULL);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
	tags_len += r;
	r = sc_asn1_put_tag(0x96, NULL, args->privkey.rsa.dmq1.len, NULL, 0, NULL);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");
	tags_len += r;

	/* Write the outer tag and length information. */
	p = sbuf;
	r = sc_asn1_put_tag(0x7F48, NULL, tags_len, p, sizeof(sbuf), &p);
	LOG_TEST_RET(card->ctx, r, "Error handling TLV.");

	/* Write inner tags. */
	/* p */
	r = sc_asn1_put_tag(0x92, args->privkey.rsa.p.value, args->privkey.rsa.p.len, p, sizeof(sbuf) - (p - sbuf), &p);
	if(r < 0)
		goto out;
	/* q */
	r = sc_asn1_put_tag(0x93, args->privkey.rsa.q.value, args->privkey.rsa.q.len, p, sizeof(sbuf) - (p - sbuf), &p);
	if(r < 0)
		goto out;
	/* 1/q mod p */
	r = sc_asn1_put_tag(0x94, args->privkey.rsa.iqmp.value, args->privkey.rsa.iqmp.len, p, sizeof(sbuf) - (p - sbuf), &p);
	if(r < 0)
		goto out;
	/* d mod (p-1) */
	r = sc_asn1_put_tag(0x95, args->privkey.rsa.dmp1.value, args->privkey.rsa.dmp1.len, p, sizeof(sbuf) - (p - sbuf), &p);
	if(r < 0)
		goto out;
	/* d mod (q-1) */
	r = sc_asn1_put_tag(0x96, args->privkey.rsa.dmq1.value, args->privkey.rsa.dmq1.len, p, sizeof(sbuf) - (p - sbuf), &p);
	if(r < 0)
		goto out;

	/* Send to card, using chaining or extended APDUs. */
	sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xDB, 0x3F, 0xFF);
	apdu.data = sbuf;
	apdu.datalen = p - sbuf;
	apdu.lc = p - sbuf;
	if ((card->caps & SC_CARD_CAP_APDU_EXT) == 0)
	{
		/* The lower layers will automatically do chaining */
		apdu.flags |= SC_APDU_FLAGS_CHAINING;
	}
	r = sc_transmit_apdu(card, &apdu);
	if(r < 0)
		goto out;
	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	if(apdu.sw1 == 0x6A && apdu.sw2 == 0x81)
	{
		sc_log(card->ctx, "Key import 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 import EC keys not supported by your java card. "
		       "In this case, look for supported field lengths and whether FP and/or F2M are supported. "
		       "If you tried to import a private RSA key, check the key length.");
	}
	if(apdu.sw1 == 0x69 && apdu.sw2 == 0x00)
	{
		sc_log(card->ctx, "Key import not allowed by the applet's security policy. "
		       "If you want to allow key import, set DEF_PRIVATE_KEY_IMPORT_ALLOWED in the IsoApplet,"
		       " rebuild and reinstall the applet.");
	}
	if(r < 0)
		goto out;

	r = SC_SUCCESS;
out:
	sc_mem_clear(sbuf, sizeof(sbuf));
	LOG_FUNC_RETURN(card->ctx, r);
}
Exemplo n.º 22
0
int
iasecc_sm_external_authentication(struct sc_card *card, unsigned skey_ref, int *tries_left)
{
	struct sc_context *ctx = card->ctx;
#ifdef ENABLE_SM
	struct sm_info *sm_info = &card->sm_ctx.info;
	struct sm_cwa_session *cwa_session = &sm_info->session.cwa;
	struct sc_remote_data rdata;
	struct sc_apdu apdu;
	unsigned char sbuf[0x100];
	int rv, offs;

	LOG_FUNC_CALLED(ctx);
	sc_log(ctx, "iasecc_sm_external_authentication(): SKey ref %i", skey_ref);

	if (card->sm_ctx.sm_mode == SM_MODE_NONE)
		LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "Cannot do 'External Authentication' without SM activated ");

	strncpy(sm_info->config_section, card->sm_ctx.config_section, sizeof(sm_info->config_section));
	sm_info->cmd = SM_CMD_EXTERNAL_AUTH;
	sm_info->serialnr = card->serialnr;
	sm_info->card_type = card->type;
	sm_info->sm_type = SM_TYPE_CWA14890;
	cwa_session->params.crt_at.usage = IASECC_UQB_AT_EXTERNAL_AUTHENTICATION;
	cwa_session->params.crt_at.algo = IASECC_ALGORITHM_ROLE_AUTH;
	cwa_session->params.crt_at.refs[0] = skey_ref;

	offs = 0;
	sbuf[offs++] = IASECC_CRT_TAG_ALGO;
	sbuf[offs++] = 0x01;
	sbuf[offs++] = IASECC_ALGORITHM_ROLE_AUTH;
	sbuf[offs++] = IASECC_CRT_TAG_REFERENCE;
	sbuf[offs++] = 0x01;
	sbuf[offs++] = skey_ref;

	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x81, 0xA4);
	apdu.data = sbuf;
	apdu.datalen = offs;
	apdu.lc = offs;

	rv = sc_transmit_apdu(card, &apdu);
	LOG_TEST_RET(ctx, rv, "iasecc_sm_external_authentication(): APDU transmit failed");
	rv = sc_check_sw(card, apdu.sw1, apdu.sw2);
	LOG_TEST_RET(ctx, rv, "iasecc_sm_external_authentication(): set SE error");

	rv = sc_get_challenge(card, cwa_session->card_challenge, sizeof(cwa_session->card_challenge));
	LOG_TEST_RET(ctx, rv, "iasecc_sm_external_authentication(): set SE error");

	sc_remote_data_init(&rdata);

	if (!card->sm_ctx.module.ops.initialize)
		LOG_TEST_RET(ctx, SC_ERROR_SM_NOT_INITIALIZED, "No SM module");
	rv = card->sm_ctx.module.ops.initialize(ctx, sm_info, &rdata);
	LOG_TEST_RET(ctx, rv, "SM: INITIALIZE failed");

	sc_log(ctx, "sm_iasecc_external_authentication(): rdata length %i\n", rdata.length);

	rv = iasecc_sm_transmit_apdus (card, &rdata, NULL, 0);
	if (rv == SC_ERROR_PIN_CODE_INCORRECT && tries_left)
		*tries_left = (rdata.data + rdata.length - 1)->apdu.sw2 & 0x0F;
	LOG_TEST_RET(ctx, rv, "sm_iasecc_external_authentication(): execute failed");

	LOG_FUNC_RETURN(ctx, rv);
#else
	LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "built without support of SM and External Authentication");
	return SC_ERROR_NOT_SUPPORTED;
#endif
}
Exemplo n.º 23
0
static int cardos_init(sc_card_t *card)
{
	unsigned long	flags, rsa_2048 = 0;
	size_t data_field_length;
	sc_apdu_t apdu;
	u8 rbuf[2];

	card->name = "CardOS M4";
	card->cla = 0x00;

	/* Set up algorithm info. */
	flags = SC_ALGORITHM_NEED_USAGE
		| SC_ALGORITHM_RSA_RAW
		| SC_ALGORITHM_RSA_HASH_NONE
		| SC_ALGORITHM_ONBOARD_KEY_GEN
		;
	_sc_card_add_rsa_alg(card,  512, flags, 0);
	_sc_card_add_rsa_alg(card,  768, flags, 0);
	_sc_card_add_rsa_alg(card, 1024, flags, 0);

	if (card->type == SC_CARD_TYPE_CARDOS_M4_2) {
		int r = cardos_have_2048bit_package(card);
		if (r < 0)
			return r;
		if (r == 1)
			rsa_2048 = 1;
		card->caps |= SC_CARD_CAP_APDU_EXT;
	} else if (card->type == SC_CARD_TYPE_CARDOS_M4_3 
		|| card->type == SC_CARD_TYPE_CARDOS_M4_2B
		|| card->type == SC_CARD_TYPE_CARDOS_M4_2C
		|| card->type == SC_CARD_TYPE_CARDOS_M4_4
		|| card->type == SC_CARD_TYPE_CARDOS_V5_0) {
		rsa_2048 = 1;
		card->caps |= SC_CARD_CAP_APDU_EXT;
	}

	/* probe DATA FIELD LENGTH with GET DATA */
	sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x8D);
	apdu.le = sizeof rbuf;
	apdu.resp = rbuf;
	apdu.resplen = sizeof(rbuf);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,
			sc_transmit_apdu(card, &apdu),
			"APDU transmit failed");
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL,
			sc_check_sw(card, apdu.sw1, apdu.sw2),
			"GET DATA command returned error");
	if (apdu.resplen != 2)
		return SC_ERROR_WRONG_LENGTH;
	data_field_length = ((rbuf[0] << 8) | rbuf[1]);

	/* strip the length of possible Lc and Le bytes */
	if (card->caps & SC_CARD_CAP_APDU_EXT)
		card->max_send_size = data_field_length - 6;
	else
		card->max_send_size = data_field_length - 3;
	/* strip the length of SW bytes */
	card->max_recv_size = data_field_length - 2;

	if (rsa_2048 == 1) {
		_sc_card_add_rsa_alg(card, 1280, flags, 0);
		_sc_card_add_rsa_alg(card, 1536, flags, 0);
		_sc_card_add_rsa_alg(card, 1792, flags, 0);
		_sc_card_add_rsa_alg(card, 2048, flags, 0);
	}

	return 0;
}
Exemplo n.º 24
0
static int ias_set_security_env(sc_card_t *card,
		const sc_security_env_t *env, int se_num)
{
	int 		r;
	sc_apdu_t 	apdu;
	u8 			sbuf[SC_MAX_APDU_BUFFER_SIZE];

	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "ias_set_security_env, keyRef = 0x%0x, algo = 0x%0x\n",
			*env->key_ref, env->algorithm_flags);

	assert(card != NULL && env != NULL);

	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0);
	switch (env->operation) {
	case SC_SEC_OPERATION_DECIPHER:
		apdu.p2 = 0xB8; /* confidentiality template */
		sbuf[0] = 0x95;	/* tag for usage qualifier byte */
		sbuf[1] = 0x01;	/* tag length */
		sbuf[2] = 0x40; /* data decryption */
		sbuf[3] = 0x84; /* tag for private key reference */
		sbuf[4] = 0x01; /* tag length */
		sbuf[5] = *env->key_ref;	/* key reference */
		sbuf[6] = 0x80; /* tag for algorithm reference */
		sbuf[7] = 0x01; /* tag length */
		if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)
			sbuf[8] = 0x1A; /* RSA PKCS#1 with no data formatting */
		else {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Set Sec Env: unsupported algo 0X%0X\n",
					env->algorithm_flags);
			return SC_ERROR_INVALID_ARGUMENTS;
		}
		apdu.lc = 9;
		apdu.datalen = 9;
		break;
	case SC_SEC_OPERATION_SIGN:
		apdu.p2 = 0xA4; /* authentication template */
		sbuf[0] = 0x95;	/* tag for usage qualifier byte */
		sbuf[1] = 0x01;	/* tag length */
		sbuf[2] = 0x40; /* internal authentication */
		sbuf[3] = 0x84; /* tag for private key reference */
		sbuf[4] = 0x01; /* tag length */
		sbuf[5] = *env->key_ref;	/* key reference */
		sbuf[6] = 0x80; /* tag for algorithm reference */
		sbuf[7] = 0x01; /* tag length */
		if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1)
			sbuf[8] = 0x02; /* RSA PKCS#1 with no data formatting */
		else {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Set Sec Env: unsupported algo 0X%0X\n",
					env->algorithm_flags);
			return SC_ERROR_INVALID_ARGUMENTS;
		}
		apdu.lc = 9;
		apdu.datalen = 9;
		break;
	default:
		return SC_ERROR_INVALID_ARGUMENTS;
	}
	apdu.le = 0;
	apdu.data = sbuf;
	apdu.resplen = 0;

	r = sc_transmit_apdu(card, &apdu);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Set Security Env APDU transmit failed");

	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card's Set Security Env command returned error");

	return r;
}
static int atrust_acos_compute_signature(struct sc_card *card,
				     const u8 * data, size_t datalen,
				     u8 * out, size_t outlen)
{
	int r;
	struct sc_apdu apdu;
	u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
	u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
	atrust_acos_ex_data *ex_data = (atrust_acos_ex_data *)card->drv_data;

	if (datalen > SC_MAX_APDU_BUFFER_SIZE)
		SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_INVALID_ARGUMENTS);

	if (ex_data->sec_ops == SC_SEC_OPERATION_SIGN) {
		/* compute signature with the COMPUTE SIGNATURE command */
		
		/* set the hash value     */
		sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A,
			       0x90, 0x81);
		apdu.resp = rbuf;
		apdu.resplen = sizeof(rbuf);
		apdu.le = 0;
		memcpy(sbuf, data, datalen);
		apdu.data = sbuf;
		apdu.lc = datalen;
		apdu.datalen = datalen;
		r = sc_transmit_apdu(card, &apdu);
		SC_TEST_RET(card->ctx, r, "APDU transmit failed");
		if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
			SC_FUNC_RETURN(card->ctx, 4, 
				       sc_check_sw(card, apdu.sw1, apdu.sw2));

		/* call COMPUTE SIGNATURE */
		sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x2A,
			       0x9E, 0x9A);
		apdu.resp = rbuf;
		apdu.resplen = sizeof(rbuf);
		apdu.le = 256;

		apdu.lc = 0;
		apdu.datalen = 0;
		apdu.sensitive = 1;
		r = sc_transmit_apdu(card, &apdu);
		SC_TEST_RET(card->ctx, r, "APDU transmit failed");
		if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
			size_t len = apdu.resplen > outlen ? outlen : apdu.resplen;
			memcpy(out, apdu.resp, len);
			SC_FUNC_RETURN(card->ctx, 4, len);
		}
	} else if (ex_data->sec_ops == SC_SEC_OPERATION_AUTHENTICATE) {
		size_t tmp_len;
		/* call INTERNAL AUTHENTICATE */
		sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x10, 0x00);
		/* fix/create DigestInfo structure (if necessary) */
		if (ex_data->fix_digestInfo) {
			unsigned int flags = ex_data->fix_digestInfo & SC_ALGORITHM_RSA_HASHES;
			if (flags == 0x0)
				/* XXX: assume no hash is wanted */
				flags = SC_ALGORITHM_RSA_HASH_NONE;
			tmp_len = sizeof(sbuf);
			r = sc_pkcs1_encode(card->ctx, flags, data, datalen,
					sbuf, &tmp_len, sizeof(sbuf));
			if (r < 0)
				return r;
		} else {
			memcpy(sbuf, data, datalen);
			tmp_len = datalen;
		}
		apdu.lc = tmp_len;
		apdu.data = sbuf;
		apdu.datalen = tmp_len;
		apdu.resp = rbuf;
		apdu.resplen = sizeof(rbuf);
		apdu.le = 256;
		r = sc_transmit_apdu(card, &apdu);
		SC_TEST_RET(card->ctx, r, "APDU transmit failed");
		if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
			SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2));
		{
			size_t len = apdu.resplen > outlen ? outlen : apdu.resplen;

			memcpy(out, apdu.resp, len);
			SC_FUNC_RETURN(card->ctx, 4, len);
		}
	} else
		SC_FUNC_RETURN(card->ctx, 4, SC_ERROR_INVALID_ARGUMENTS);

	/* clear old state */
	ex_data->sec_ops = 0;
	ex_data->fix_digestInfo = 0;

	SC_FUNC_RETURN(card->ctx, 4, sc_check_sw(card, apdu.sw1, apdu.sw2));
}
Exemplo n.º 26
0
static int
isoApplet_set_security_env(sc_card_t *card,
                           const sc_security_env_t *env, int se_num)
{
	sc_apdu_t apdu;
	u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
	u8 *p;
	int r;
	struct isoApplet_drv_data *drvdata = DRVDATA(card);

	LOG_FUNC_CALLED(card->ctx);

	if(se_num != 0)
	{
		LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED,
		             "IsoApplet does not support storing of security environments.");
	}
	assert(card != NULL && env != NULL);
	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 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;
	}
	p = sbuf;

	if (env->flags & SC_SEC_ENV_ALG_PRESENT)
	{

		switch(env->algorithm)
		{

		case SC_ALGORITHM_RSA:
			if( env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1 )
			{
				drvdata->sec_env_alg_ref = ISOAPPLET_ALG_REF_RSA_PAD_PKCS1;
			}
			else
			{
				LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "IsoApplet only supports RSA with PKCS1 padding.");
			}
			break;

		case SC_ALGORITHM_EC:
			if( env->algorithm_flags & SC_ALGORITHM_ECDSA_RAW )
			{
				drvdata->sec_env_alg_ref = ISOAPPLET_ALG_REF_ECDSA;
				drvdata->sec_env_ec_field_length = env->algorithm_ref;
			}
			else
			{
				LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "IsoApplet only supports raw ECDSA.");
			}
			break;

		default:
			LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "Unsupported algorithm.");
		}

		*p++ = 0x80; /* algorithm reference */
		*p++ = 0x01;
		*p++ = drvdata->sec_env_alg_ref;
	}

	if (env->flags & SC_SEC_ENV_FILE_REF_PRESENT)
	{
		*p++ = 0x81;
		*p++ = env->file_ref.len;
		assert(sizeof(sbuf) - (p - sbuf) >= env->file_ref.len);
		memcpy(p, env->file_ref.value, env->file_ref.len);
		p += env->file_ref.len;
	}

	if (env->flags & SC_SEC_ENV_KEY_REF_PRESENT)
	{
		if (env->flags & SC_SEC_ENV_KEY_REF_ASYMMETRIC)
			*p++ = 0x83;
		else
			*p++ = 0x84;
		*p++ = env->key_ref_len;
		assert(sizeof(sbuf) - (p - sbuf) >= env->key_ref_len);
		memcpy(p, env->key_ref, env->key_ref_len);
		p += env->key_ref_len;
	}
	r = p - sbuf;
	apdu.lc = r;
	apdu.datalen = r;
	apdu.data = sbuf;

	if (apdu.datalen != 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");
	}

	LOG_FUNC_RETURN(card->ctx, r);
}
Exemplo n.º 27
0
static int gemsafe_build_pin_apdu(struct sc_card *card,
                                  struct sc_apdu *apdu,
                                  struct sc_pin_cmd_data *data)
{
    static u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
    int r, len = 0, pad = 0, use_pin_pad = 0, ins, p1 = 0;

    switch (data->pin_type) {
    case SC_AC_CHV:
        break;
    default:
        return SC_ERROR_INVALID_ARGUMENTS;
    }

    if (data->flags & SC_PIN_CMD_NEED_PADDING)
        pad = 1;
    if (data->flags & SC_PIN_CMD_USE_PINPAD)
        use_pin_pad = 1;

    data->pin1.offset = 5;

    switch (data->cmd) {
    case SC_PIN_CMD_VERIFY:
        ins = 0x20;
        if ((r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0)
            return r;
        len = r;
        break;
    case SC_PIN_CMD_CHANGE:
        ins = 0x24;
        if (data->pin1.len != 0 || use_pin_pad) {
            if ((r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0)
                return r;
            len += r;
        } else {
            /* implicit test */
            p1 = 1;
        }

        data->pin2.offset = data->pin1.offset + len;
        if ((r = sc_build_pin(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0)
            return r;
        len += r;
        break;
    case SC_PIN_CMD_UNBLOCK:
        ins = 0x2C;
        if (data->pin1.len != 0 || use_pin_pad) {
            if ((r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0)
                return r;
            len += r;
        } else {
            p1 |= 0x02;
        }

        if (data->pin2.len != 0 || use_pin_pad) {
            data->pin2.offset = data->pin1.offset + len;
            if ((r = sc_build_pin(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0)
                return r;
            len += r;
        } else {
            p1 |= 0x01;
        }
        break;
    default:
        return SC_ERROR_NOT_SUPPORTED;
    }

    sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT,
                   ins, p1, data->pin_reference);

    apdu->lc = len;
    apdu->datalen = len;
    apdu->data = sbuf;
    apdu->resplen = 0;

    return 0;
}
Exemplo n.º 28
0
static int cardos_match_card(sc_card_t *card)
{
	unsigned char atr[SC_MAX_ATR_SIZE];
	int i;

	i = _sc_match_atr(card, cardos_atrs, &card->type);
	if (i < 0)
		return 0;

	memcpy(atr, card->atr.value, sizeof(atr));

	/* Do not change card type for CIE! */
	if (card->type == SC_CARD_TYPE_CARDOS_CIE_V1)
		return 1;
	if (card->type == SC_CARD_TYPE_CARDOS_M4_4)
		return 1;
	if (card->type == SC_CARD_TYPE_CARDOS_V5_0)
		return 1;
	if (card->type == SC_CARD_TYPE_CARDOS_M4_2) {
		int rv;
		sc_apdu_t apdu;
		u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
		/* first check some additional ATR bytes */
		if ((atr[4] != 0xff && atr[4] != 0x02) ||
		    (atr[6] != 0x10 && atr[6] != 0x0a) ||
		    (atr[9] != 0x55 && atr[9] != 0x58))
			return 0;
		/* get the os version using GET DATA and compare it with
		 * version in the ATR */
		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "checking cardos version ...");
		sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x82);
		apdu.resp = rbuf;
		apdu.resplen = sizeof(rbuf);
		apdu.le = 256;
		apdu.lc = 0;
		rv = sc_transmit_apdu(card, &apdu);
		SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, rv, "APDU transmit failed");
		if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00)
			return 0;
		if (apdu.resp[0] != atr[10] ||
		    apdu.resp[1] != atr[11])
			/* version mismatch */
			return 0;
		if (atr[11] <= 0x04) {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos m4.01");
			card->type = SC_CARD_TYPE_CARDOS_M4_01;
		} else if (atr[11] == 0x08) {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos v4.3b");
			card->type = SC_CARD_TYPE_CARDOS_M4_3;
		} else if (atr[11] == 0x09) {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos v4.2b");
			card->type = SC_CARD_TYPE_CARDOS_M4_2B;
		} else if (atr[11] >= 0x0B) {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos v4.2c or higher");
			card->type = SC_CARD_TYPE_CARDOS_M4_2C;
		} else {
			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "found cardos m4.2");
		}
	}
	return 1;
}
Exemplo n.º 29
0
static int starcos_select_fid(sc_card_t *card,
                              unsigned int id_hi, unsigned int id_lo,
                              sc_file_t **file_out)
{
    sc_apdu_t apdu;
    u8 data[] = {id_hi & 0xff, id_lo & 0xff};
    u8 resp[SC_MAX_APDU_BUFFER_SIZE];
    int bIsDF = 0, r;

    /* request FCI to distinguish between EFs and DFs */
    sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x00, 0x00);
    apdu.p2   = 0x00;
    apdu.resp = (u8*)resp;
    apdu.resplen = SC_MAX_APDU_BUFFER_SIZE;
    apdu.le = 256;
    apdu.lc = 2;
    apdu.data = (u8*)data;
    apdu.datalen = 2;

    r = sc_transmit_apdu(card, &apdu);
    SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");

    if (apdu.p2 == 0x00 && apdu.sw1 == 0x62 && apdu.sw2 == 0x84 ) {
        /* no FCI => we have a DF (see comment in process_fci()) */
        bIsDF = 1;
        apdu.p2 = 0x0C;
        apdu.cse = SC_APDU_CASE_3_SHORT;
        apdu.resplen = 0;
        apdu.le = 0;
        r = sc_transmit_apdu(card, &apdu);
        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU re-transmit failed");
    } else if (apdu.sw1 == 0x61 || (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)) {
        /* SELECT returned some data (possible FCI) =>
         * try a READ BINARY to see if a EF is selected */
        sc_apdu_t apdu2;
        u8 resp2[2];
        sc_format_apdu(card, &apdu2, SC_APDU_CASE_2_SHORT, 0xB0, 0, 0);
        apdu2.resp = (u8*)resp2;
        apdu2.resplen = 2;
        apdu2.le = 1;
        apdu2.lc = 0;
        r = sc_transmit_apdu(card, &apdu2);
        SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
        if (apdu2.sw1 == 0x69 && apdu2.sw2 == 0x86)
            /* no current EF is selected => we have a DF */
            bIsDF = 1;
    }

    if (apdu.sw1 != 0x61 && (apdu.sw1 != 0x90 || apdu.sw2 != 0x00))
        SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));

    /* update cache */
    if (bIsDF) {
        card->cache.current_path.type = SC_PATH_TYPE_PATH;
        card->cache.current_path.value[0] = 0x3f;
        card->cache.current_path.value[1] = 0x00;
        if (id_hi == 0x3f && id_lo == 0x00)
            card->cache.current_path.len = 2;
        else {
            card->cache.current_path.len = 4;
            card->cache.current_path.value[2] = id_hi;
            card->cache.current_path.value[3] = id_lo;
        }
    }

    if (file_out) {
        sc_file_t *file = sc_file_new();
        if (!file)
            SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY);
        file->id   = (id_hi << 8) + id_lo;
        file->path = card->cache.current_path;

        if (bIsDF) {
            /* we have a DF */
            file->type = SC_FILE_TYPE_DF;
            file->ef_structure = SC_FILE_EF_UNKNOWN;
            file->size = 0;
            file->namelen = 0;
            file->magic = SC_FILE_MAGIC;
            *file_out = file;
        } else {
            /* ok, assume we have a EF */
            r = process_fci(card->ctx, file, apdu.resp,
                            apdu.resplen);
            if (r != SC_SUCCESS) {
                sc_file_free(file);
                return r;
            }

            *file_out = file;
        }
    }

    SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_SUCCESS);
}
Exemplo n.º 30
0
static int ias_build_pin_apdu(sc_card_t *card,
		sc_apdu_t *apdu,
		struct sc_pin_cmd_data *data)
{
	static u8 	sbuf[SC_MAX_APDU_BUFFER_SIZE];
	int 		r, len, pad, use_pin_pad, ins, p1;

	len = pad = use_pin_pad = p1 = 0;
	assert(card != NULL);

	switch (data->pin_type) {
	case SC_AC_CHV:
		break;
	default:
		return SC_ERROR_INVALID_ARGUMENTS;
	}

	if (data->flags & SC_PIN_CMD_USE_PINPAD)
		use_pin_pad = 1;
	/* "needs-padding" necessary for the PTEID card,
	 * but not defined in the pin structure
	 */
	if ((data->flags & SC_PIN_CMD_NEED_PADDING) ||
		 card->type == SC_CARD_TYPE_IAS_PTEID)
		pad = 1;

	data->pin1.offset = 5;

	switch (data->cmd) {
	case SC_PIN_CMD_VERIFY:
		ins = 0x20;
		if ( (r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0)
			return r;
		len = r;
		break;
	case SC_PIN_CMD_CHANGE:
		ins = 0x24;
		if ((data->flags & SC_PIN_CMD_IMPLICIT_CHANGE) == 0 &&
			(data->pin1.len != 0 || use_pin_pad)) {
			if ( (r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0)
				return r;
			len += r;
		} else {
			/* implicit test */
			p1 = 1;
		}
		data->pin2.offset = data->pin1.offset + len;
		if ( (r = sc_build_pin(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0)
			return r;
		len += r;
		break;
	case SC_PIN_CMD_UNBLOCK:
		ins = 0x2C;
		if (data->pin1.len != 0 || use_pin_pad) {
			if ( (r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0)
				return r;
			len += r;
		} else {
			p1 |= 0x02;
		}
		if (data->pin2.len != 0 || use_pin_pad) {
			data->pin2.offset = data->pin1.offset + len;
			if ( (r = sc_build_pin(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0)
				return r;
			len += r;
		} else {
			p1 |= 0x01;
		}
		break;
	default:
		return SC_ERROR_NOT_SUPPORTED;
	}

	sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, ins, p1, data->pin_reference);
	apdu->lc = len;
	apdu->datalen = len;
	apdu->data = sbuf;
	apdu->resplen = 0;

	return SC_SUCCESS;
}