Blob Blame History Raw
diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am
index 49d122682f..a9f200c959 100644
--- a/src/libopensc/Makefile.am
+++ b/src/libopensc/Makefile.am
@@ -48,14 +48,14 @@ libopensc_la_SOURCES_BASE = \
 	card-iasecc.c iasecc-sdo.c iasecc-sm.c card-sc-hsm.c \
 	card-dnie.c cwa14890.c cwa-dnie.c \
 	card-isoApplet.c card-masktech.c card-gids.c card-jpki.c \
-	card-npa.c card-esteid2018.c \
+	card-npa.c card-esteid2018.c card-idprime.c \
 	\
 	pkcs15-openpgp.c pkcs15-starcert.c \
 	pkcs15-tcos.c pkcs15-esteid.c pkcs15-gemsafeGPK.c \
 	pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \
 	pkcs15-cac.c pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c \
 	pkcs15-oberthur.c pkcs15-itacns.c pkcs15-gemsafeV1.c pkcs15-sc-hsm.c \
-	pkcs15-coolkey.c pkcs15-din-66291.c \
+	pkcs15-coolkey.c pkcs15-din-66291.c pkcs15-idprime.c \
 	pkcs15-dnie.c pkcs15-gids.c pkcs15-iasecc.c pkcs15-jpki.c pkcs15-esteid2018.c \
 	compression.c p15card-helper.c sm.c \
 	aux-data.c
@@ -131,14 +131,14 @@ TIDY_FILES = \
 	card-iasecc.c iasecc-sdo.c iasecc-sm.c card-sc-hsm.c \
 	cwa14890.c cwa-dnie.c \
 	card-isoApplet.c card-masktech.c card-jpki.c \
-	card-npa.c card-esteid2018.c \
+	card-npa.c card-esteid2018.c card-idprime.c \
 	\
 	pkcs15-openpgp.c \
 	pkcs15-tcos.c pkcs15-esteid.c \
 	pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c \
 	pkcs15-cac.c pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c \
 	pkcs15-oberthur.c pkcs15-itacns.c pkcs15-sc-hsm.c \
-	pkcs15-coolkey.c pkcs15-din-66291.c \
+	pkcs15-coolkey.c pkcs15-din-66291.c pkcs15-idprime.c \
 	pkcs15-dnie.c pkcs15-gids.c pkcs15-iasecc.c pkcs15-jpki.c pkcs15-esteid2018.c \
 	compression.c p15card-helper.c sm.c \
 	aux-data.c \
diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak
index 487fbeb4a6..2e3c30c22b 100644
--- a/src/libopensc/Makefile.mak
+++ b/src/libopensc/Makefile.mak
@@ -27,7 +27,7 @@ OBJECTS			= \
 	card-iasecc.obj iasecc-sdo.obj iasecc-sm.obj cwa-dnie.obj cwa14890.obj \
 	card-sc-hsm.obj card-dnie.obj card-isoApplet.obj pkcs15-coolkey.obj \
 	card-masktech.obj card-gids.obj card-jpki.obj \
-	card-npa.obj card-esteid2018.obj \
+	card-npa.obj card-esteid2018.obj card-idprime.obj \
 	\
 	pkcs15-openpgp.obj pkcs15-starcert.obj \
 	pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-gemsafeGPK.obj \
@@ -35,7 +35,7 @@ OBJECTS			= \
 	pkcs15-cac.obj pkcs15-esinit.obj pkcs15-westcos.obj pkcs15-pteid.obj pkcs15-din-66291.obj \
 	pkcs15-oberthur.obj pkcs15-itacns.obj pkcs15-gemsafeV1.obj pkcs15-sc-hsm.obj \
 	pkcs15-dnie.obj pkcs15-gids.obj pkcs15-iasecc.obj pkcs15-jpki.obj \
-	pkcs15-esteid2018.obj \
+	pkcs15-esteid2018.obj pkcs15-idprime.obj \
 	compression.obj p15card-helper.obj sm.obj \
 	aux-data.obj \
 	$(TOPDIR)\win32\versioninfo.res
diff --git a/src/libopensc/card-cac.c b/src/libopensc/card-cac.c
index d59b4337c8..2c9361df88 100644
--- a/src/libopensc/card-cac.c
+++ b/src/libopensc/card-cac.c
@@ -54,6 +54,7 @@
 #endif
 #include "iso7816.h"
 #include "card-cac-common.h"
+#include "pkcs15.h"
 
 /*
  *  CAC hardware and APDU constants
diff --git a/src/libopensc/card-cac1.c b/src/libopensc/card-cac1.c
index 08d62b62cc..67035d64e6 100644
--- a/src/libopensc/card-cac1.c
+++ b/src/libopensc/card-cac1.c
@@ -54,6 +54,7 @@
 #endif
 #include "iso7816.h"
 #include "card-cac-common.h"
+#include "pkcs15.h"
 
 /*
  *  CAC hardware and APDU constants
diff --git a/src/libopensc/card-idprime.c b/src/libopensc/card-idprime.c
new file mode 100644
index 0000000000..7399830afd
--- /dev/null
+++ b/src/libopensc/card-idprime.c
@@ -0,0 +1,803 @@
+/*
+ * card-idprime.c: Support for Gemalto IDPrime smart cards
+ *
+ * Copyright (c) 2019 Red Hat, Inc.
+ *
+ * Author: Jakub Jelen <jjelen@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "internal.h"
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef ENABLE_ZLIB
+#include "compression.h"
+#endif
+
+#include "cardctl.h"
+#include "pkcs15.h"
+
+static const struct sc_card_operations *iso_ops = NULL;
+
+static struct sc_card_operations idprime_ops;
+static struct sc_card_driver idprime_drv = {
+	"Gemalto IDPrime",
+	"idprime",
+	&idprime_ops,
+	NULL, 0, NULL
+};
+
+/* This ATR says, there is no EF.DIR nor EF.ATR so ISO discovery mechanisms
+ * are not useful here */
+static const struct sc_atr_table idprime_atrs[] = {
+	{ "3b:7f:96:00:00:80:31:80:65:b0:84:41:3d:f6:12:0f:fe:82:90:00",
+	  "ff:ff:00:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:ff:ff:ff:ff:ff:ff",
+	  "Gemalto IDPrime MD 8840, 3840, 3810, 840 and 830 Cards",
+	  SC_CARD_TYPE_IDPRIME_GENERIC, 0, NULL },
+};
+
+static const sc_path_t idprime_path = {
+	"", 0,
+	0, 0, SC_PATH_TYPE_DF_NAME,
+	{ "\xA0\x00\x00\x00\x18\x80\x00\x00\x00\x06\x62", 11 }
+};
+
+/* data structures to store meta data about IDPrime objects */
+typedef struct idprime_object {
+	int fd;
+	unsigned char key_reference;
+	u8 df[2];
+	unsigned short length;
+} idprime_object_t;
+
+/*
+ * IDPrime private data per card state
+ */
+typedef struct idprime_private_data {
+	u8 *cache_buf;			/* cached version of the currently selected file */
+	size_t cache_buf_len;		/* length of the cached selected file */
+	int cached;			/* is the cached selected file valid */
+	size_t file_size;		/* this is real file size since IDPrime is quite strict about lengths */
+	list_t pki_list;		/* list of pki containers */
+	idprime_object_t *pki_current;	/* current pki object _ctl function */
+	int tinfo_present;		/* Token Info Label object is present*/
+	u8 tinfo_df[2];			/* DF of object with Token Info Label */
+} idprime_private_data_t;
+
+/* For SimCList autocopy, we need to know the size of the data elements */
+static size_t idprime_list_meter(const void *el) {
+	return sizeof(idprime_object_t);
+}
+
+void idprime_free_private_data(idprime_private_data_t *priv)
+{
+	free(priv->cache_buf);
+	list_destroy(&priv->pki_list);
+	free(priv);
+	return;
+}
+
+idprime_private_data_t *idprime_new_private_data(void)
+{
+	idprime_private_data_t *priv;
+
+	priv = calloc(1, sizeof(idprime_private_data_t));
+	if (priv == NULL)
+		return NULL;
+
+	/* Initialize PKI Applets list */
+	if (list_init(&priv->pki_list) != 0 ||
+	    list_attributes_copy(&priv->pki_list, idprime_list_meter, 1) != 0) {
+		idprime_free_private_data(priv);
+		return NULL;
+	}
+
+	return priv;
+}
+
+int idprime_add_object_to_list(list_t *list, const idprime_object_t *object)
+{
+	if (list_append(list, object) < 0)
+		return SC_ERROR_INTERNAL;
+	return SC_SUCCESS;
+}
+
+/* This selects main IDPrime AID which is used for communication with
+ * the card */
+static int idprime_select_idprime(sc_card_t *card)
+{
+	return iso_ops->select_file(card, &idprime_path, NULL);
+}
+
+/* This select some index file, which is useful for enumerating other files
+ * on the card */
+static int idprime_select_index(sc_card_t *card)
+{
+	int r;
+	sc_file_t *file = NULL;
+	sc_path_t index_path;
+
+	/* First, we need to make sure the IDPrime AID is selected */
+	r = idprime_select_idprime(card);
+	if (r != SC_SUCCESS) {
+		LOG_FUNC_RETURN(card->ctx, r);
+	}
+
+	/* Returns FCI with expected length of data */
+	sc_format_path("0101", &index_path);
+	r = iso_ops->select_file(card, &index_path, &file);
+	if (r == SC_SUCCESS) {
+		r = file->size;
+	}
+	sc_file_free(file);
+	/* Ignore too large files */
+	if (r > MAX_FILE_SIZE) {
+		r = SC_ERROR_INVALID_DATA;
+	}
+	return r;
+}
+
+static int idprime_process_index(sc_card_t *card, idprime_private_data_t *priv, int length)
+{
+	u8 *buf = NULL;
+	int r = SC_ERROR_OUT_OF_MEMORY;
+	int i, num_entries;
+	idprime_object_t new_object;
+
+	buf = malloc(length);
+	if (buf == NULL) {
+		goto done;
+	}
+
+	r = iso_ops->read_binary(card, 0, buf, length, 0);
+	if (r < 1) {
+		r = SC_ERROR_WRONG_LENGTH;
+		goto done;
+	}
+
+	/* First byte shows the number of entries, each of them 21 bytes long */
+	num_entries = buf[0];
+	if (r < num_entries*21 + 1) {
+		r = SC_ERROR_INVALID_DATA;
+		goto done;
+	}
+	new_object.fd = 0;
+	for (i = 0; i < num_entries; i++) {
+		u8 *start = &buf[i*21+1];
+
+		/* First two bytes specify the object DF */
+		new_object.df[0] = start[0];
+		new_object.df[1] = start[1];
+		/* Second two bytes refer to the object size */
+		new_object.length = bebytes2ushort(&start[2]);
+		sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "df=%s, len=%u",
+			sc_dump_hex(new_object.df, sizeof(new_object.df)), new_object.length);
+		/* in minidriver, mscp/kxcNN or kscNN lists certificates */
+		if (((memcmp(&start[4], "ksc", 3) == 0) || memcmp(&start[4], "kxc", 3) == 0)
+			&& (memcmp(&start[12], "mscp", 5) == 0)) {
+			new_object.fd++;
+			if (card->type == SC_CARD_TYPE_IDPRIME_V2) {
+				/* The key reference starts from 0x11 */
+				new_object.key_reference = 0x10 + new_object.fd;
+			} else {
+				/* The key reference is one bigger than the value found here for some reason */
+				new_object.key_reference = start[8] + 1;
+			}
+			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Found certificate with fd=%d, key_ref=%d",
+				new_object.fd, new_object.key_reference);
+			idprime_add_object_to_list(&priv->pki_list, &new_object);
+
+		/* This looks like non-standard extension listing pkcs11 token info label in my card */
+		} else if ((memcmp(&start[4], "tinfo", 6) == 0) && (memcmp(&start[12], "p11", 4) == 0)) {
+			memcpy(priv->tinfo_df, new_object.df, sizeof(priv->tinfo_df));
+			priv->tinfo_present = 1;
+			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Found p11/tinfo object");
+		}
+	}
+	r = SC_SUCCESS;
+done:
+	free(buf);
+	LOG_FUNC_RETURN(card->ctx, r);
+}
+
+/* CPLC has 42 bytes, but we get it with 3B header */
+#define CPLC_LENGTH 45
+static int idprime_init(sc_card_t *card)
+{
+	int r;
+	unsigned long flags;
+	idprime_private_data_t *priv = NULL;
+	struct sc_apdu apdu;
+	u8 rbuf[CPLC_LENGTH];
+	size_t rbuflen = sizeof(rbuf);
+
+	/* We need to differentiate the OS version since they behave slightly differently */
+	sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0xCA, 0x9F, 0x7F);
+	apdu.resp = rbuf;
+	apdu.resplen = rbuflen;
+	apdu.le = rbuflen;
+	r = sc_transmit_apdu(card, &apdu);
+	card->type = SC_CARD_TYPE_IDPRIME_GENERIC;
+	if (r == SC_SUCCESS && apdu.resplen == CPLC_LENGTH) {
+		/* We are interested in the OS release level here */
+		switch (rbuf[11]) {
+		case 0x01:
+			card->type = SC_CARD_TYPE_IDPRIME_V1;
+			sc_log(card->ctx, "Detected IDPrime applet version 1");
+			break;
+		case 0x02:
+			card->type = SC_CARD_TYPE_IDPRIME_V2;
+			sc_log(card->ctx, "Detected IDPrime applet version 2");
+			break;
+		default:
+			sc_log(card->ctx, "Unknown OS version received: %d", rbuf[11]);
+			break;
+		}
+	} else {
+		sc_log(card->ctx, "Failed to get CPLC data or invalid length returned, "
+			"err=%d, len=%"SC_FORMAT_LEN_SIZE_T"u",
+			r, apdu.resplen);
+	}
+
+	/* Now, select and process the index file */
+	r = idprime_select_index(card);
+	if (r <= 0) {
+		LOG_FUNC_RETURN(card->ctx, r);
+	}
+
+	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Index file found");
+
+	priv = idprime_new_private_data();
+	if (!priv) {
+		return SC_ERROR_OUT_OF_MEMORY;
+	}
+
+	r = idprime_process_index(card, priv, r);
+	if (r != SC_SUCCESS) {
+		idprime_free_private_data(priv);
+		LOG_FUNC_RETURN(card->ctx, r);
+	}
+
+	card->drv_data = priv;
+
+	switch (card->type) {
+	case SC_CARD_TYPE_IDPRIME_V1:
+		card->name = "Gemalto IDPrime (OSv1)";
+		break;
+	case SC_CARD_TYPE_IDPRIME_V2:
+		card->name = "Gemalto IDPrime (OSv2)";
+		break;
+	case SC_CARD_TYPE_IDPRIME_GENERIC:
+	default:
+		card->name = "Gemalto IDPrime (generic)";
+		break;
+	}
+	card->cla = 0x00;
+
+	/* Set up algorithm info. */
+	flags = SC_ALGORITHM_RSA_PAD_PKCS1
+		| SC_ALGORITHM_RSA_PAD_PSS
+		| SC_ALGORITHM_RSA_PAD_OAEP
+		/* SHA-1 mechanisms are not allowed in the card I have */
+		| (SC_ALGORITHM_RSA_HASH_SHA256 | SC_ALGORITHM_RSA_HASH_SHA384 | SC_ALGORITHM_RSA_HASH_SHA512)
+		| (SC_ALGORITHM_MGF1_SHA256 | SC_ALGORITHM_MGF1_SHA384 | SC_ALGORITHM_MGF1_SHA512)
+		;
+
+	_sc_card_add_rsa_alg(card, 1024, flags, 0);
+	_sc_card_add_rsa_alg(card, 2048, flags, 0);
+
+	card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO;
+
+	LOG_FUNC_RETURN(card->ctx, 0);
+}
+
+static int idprime_finish(sc_card_t *card)
+{
+	idprime_private_data_t * priv = card->drv_data;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+	if (priv) {
+		idprime_free_private_data(priv);
+	}
+	return SC_SUCCESS;
+}
+
+static int idprime_match_card(sc_card_t *card)
+{
+	int i, r;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+	i = _sc_match_atr(card, idprime_atrs, &card->type);
+	if (i < 0)
+		return 0;
+
+	r = idprime_select_index(card);
+	return (r > 0);
+}
+
+/* initialize getting a list and return the number of elements in the list */
+static int idprime_get_init_and_get_count(list_t *list, idprime_object_t **entry, int *countp)
+{
+	if (countp == NULL || entry == NULL) {
+		return SC_ERROR_INVALID_ARGUMENTS;
+	}
+	*countp = list_size(list);
+	list_iterator_start(list);
+	*entry = list_iterator_next(list);
+	return SC_SUCCESS;
+}
+
+/* finalize the list iterator */
+static int idprime_final_iterator(list_t *list)
+{
+	list_iterator_stop(list);
+	return SC_SUCCESS;
+}
+
+/* fill in the prkey_info for the current object on the list and advance to the next object */
+static int idprime_fill_prkey_info(list_t *list, idprime_object_t **entry, sc_pkcs15_prkey_info_t *prkey_info)
+{
+	memset(prkey_info, 0, sizeof(sc_pkcs15_prkey_info_t));
+	if (*entry == NULL) {
+		return SC_ERROR_FILE_END_REACHED;
+	}
+
+	prkey_info->path.len = sizeof((*entry)->df);
+	memcpy(prkey_info->path.value, (*entry)->df, sizeof((*entry)->df));
+	prkey_info->path.type = SC_PATH_TYPE_FILE_ID;
+	/* Do not specify the length -- it will be read from the FCI */
+	prkey_info->path.count = -1;
+
+	/* TODO figure out the IDs as the original driver? */
+	prkey_info->id.value[0] = ((*entry)->fd >> 8) & 0xff;
+	prkey_info->id.value[1] = (*entry)->fd & 0xff;
+	prkey_info->id.len = 2;
+	prkey_info->key_reference = (*entry)->key_reference;
+	*entry = list_iterator_next(list);
+	return SC_SUCCESS;
+}
+
+#define IDPRIME_CARDID_LEN 16
+
+static int idprime_get_serial(sc_card_t* card, sc_serial_number_t* serial)
+{
+	sc_path_t cardid_path;
+	sc_file_t *file = NULL;
+	u8 buf[IDPRIME_CARDID_LEN];
+	int r;
+
+	LOG_FUNC_CALLED(card->ctx);
+
+	/* XXX this is assumed to be cardid for windows. It can be read from the index file */
+	sc_format_path("0201", &cardid_path);
+	r = iso_ops->select_file(card, &cardid_path, &file);
+	if (r != SC_SUCCESS || file->size != IDPRIME_CARDID_LEN) { /* The cardid is always 16 B */
+		sc_file_free(file);
+		LOG_FUNC_RETURN(card->ctx, SC_ERROR_WRONG_LENGTH);
+	}
+
+	r = iso_ops->read_binary(card, 0, buf, file->size, 0);
+	sc_file_free(file);
+	if (r < 1) {
+		LOG_FUNC_RETURN(card->ctx, r);
+	} else if (r != IDPRIME_CARDID_LEN) {
+		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA);
+	}
+
+	serial->len = MIN(IDPRIME_CARDID_LEN, SC_MAX_SERIALNR);
+	memcpy(serial->value, buf, serial->len);
+	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
+}
+
+static int idprime_get_token_name(sc_card_t* card, char** tname)
+{
+	idprime_private_data_t * priv = card->drv_data;
+	sc_path_t tinfo_path = {"\x00\x00", 2, 0, 0, SC_PATH_TYPE_PATH, {"", 0}};
+	sc_file_t *file = NULL;
+	u8 buf[2];
+	int r;
+
+	LOG_FUNC_CALLED(card->ctx);
+
+	if (tname == NULL) {
+		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_ARGUMENTS);
+	}
+
+	if (!priv->tinfo_present) {
+		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
+	}
+
+	memcpy(tinfo_path.value, priv->tinfo_df, 2);
+	r = iso_ops->select_file(card, &tinfo_path, &file);
+	if (r != SC_SUCCESS || file->size == 0) {
+		sc_file_free(file);
+		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
+	}
+
+	/* First two bytes lists 0x01, the second indicates length */
+	r = iso_ops->read_binary(card, 0, buf, 2, 0);
+	if (r < 2 || buf[1] > file->size) { /* make sure we do not overrun */
+		sc_file_free(file);
+		LOG_FUNC_RETURN(card->ctx, r);
+	}
+	sc_file_free(file);
+
+	*tname = malloc(buf[1]);
+	if (*tname == NULL) {
+		LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
+	}
+
+	r = iso_ops->read_binary(card, 2, (unsigned char *)*tname, buf[1], 0);
+	if (r < 1) {
+		free(*tname);
+		LOG_FUNC_RETURN(card->ctx, r);
+	}
+
+	if ((*tname)[r-1] != '\0') {
+		(*tname)[r-1] = '\0';
+	}
+	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
+}
+
+static int idprime_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
+{
+	idprime_private_data_t * priv = card->drv_data;
+
+	LOG_FUNC_CALLED(card->ctx);
+	sc_log(card->ctx, "cmd=%ld ptr=%p", cmd, ptr);
+
+	if (priv == NULL) {
+		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
+	}
+	switch (cmd) {
+		case SC_CARDCTL_GET_SERIALNR:
+			return idprime_get_serial(card, (sc_serial_number_t *) ptr);
+		case SC_CARDCTL_IDPRIME_GET_TOKEN_NAME:
+			return idprime_get_token_name(card, (char **) ptr);
+		case SC_CARDCTL_IDPRIME_INIT_GET_OBJECTS:
+			return idprime_get_init_and_get_count(&priv->pki_list, &priv->pki_current,
+				(int *)ptr);
+		case SC_CARDCTL_IDPRIME_GET_NEXT_OBJECT:
+			return idprime_fill_prkey_info(&priv->pki_list, &priv->pki_current,
+				(sc_pkcs15_prkey_info_t *)ptr);
+		case SC_CARDCTL_IDPRIME_FINAL_GET_OBJECTS:
+			return idprime_final_iterator(&priv->pki_list);
+	}
+
+	LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
+}
+
+#define HEADER_LEN 4
+
+static int idprime_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out)
+{
+	int r, len;
+	idprime_private_data_t * priv = card->drv_data;
+	u8 data[HEADER_LEN];
+	size_t data_len = HEADER_LEN;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	/* forget any old cached values */
+	if (priv->cache_buf) {
+		free(priv->cache_buf);
+		priv->cache_buf = NULL;
+	}
+	priv->cache_buf_len = 0;
+	priv->cached = 0;
+
+	r = iso_ops->select_file(card, in_path, file_out);
+	if (r == SC_SUCCESS && priv && file_out != NULL) {
+		/* Try to read first bytes of the file to fix FCI in case of
+		 * compressed certififcate */
+		len = iso_ops->read_binary(card, 0, data, data_len, 0);
+		if (len == HEADER_LEN && data[0] == 0x01 && data[1] == 0x00) {
+			/* Cache the real file size for the caching read_binary() */
+			priv->file_size = (*file_out)->size;
+			/* Fix the information in the file structure to not confuse upper layers */
+			(*file_out)->size = (data[3]<<8) | data[2];
+		}
+	}
+	/* Return the exit code of the select command */
+	return r;
+}
+
+// used to read existing certificates
+static int idprime_read_binary(sc_card_t *card, unsigned int offset,
+	unsigned char *buf, size_t count, unsigned long flags)
+{
+	struct idprime_private_data *priv = card->drv_data;
+	int r;
+	int size;
+
+	sc_log(card->ctx, "called; %"SC_FORMAT_LEN_SIZE_T"u bytes at offset %d",
+		count, offset);
+
+	if (!priv->cached && offset == 0) {
+		// this function is called to read and uncompress the certificate
+		u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE];
+		if (sizeof(buffer) < count) {
+			LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
+		}
+		/* Read what was reported by FCI from select command */
+		r = iso_ops->read_binary(card, 0, buffer, priv->file_size, flags);
+		if (r < 0) {
+			LOG_FUNC_RETURN(card->ctx, r);
+		}
+		if (r < 4) {
+			LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA);
+		}
+		if (buffer[0] == 1 && buffer[1] == 0) {
+#ifdef ENABLE_ZLIB
+			size_t expectedsize = buffer[2] + buffer[3] * 0x100;
+			r = sc_decompress_alloc(&priv->cache_buf, &(priv->cache_buf_len),
+				buffer+4, priv->file_size-4, COMPRESSION_AUTO);
+			if (r != SC_SUCCESS) {
+				sc_log(card->ctx, "Zlib error: %d", r);
+				LOG_FUNC_RETURN(card->ctx, r);
+			}
+			if (priv->cache_buf_len != expectedsize) {
+				sc_log(card->ctx,
+					 "expected size: %"SC_FORMAT_LEN_SIZE_T"u real size: %"SC_FORMAT_LEN_SIZE_T"u",
+					 expectedsize, priv->cache_buf_len);
+				LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA);
+			}
+#else
+			sc_log(card->ctx, "compression not supported, no zlib");
+			return SC_ERROR_NOT_SUPPORTED;
+#endif /* ENABLE_ZLIB */
+		} else {
+			/* assuming uncompressed certificate */
+			priv->cache_buf = malloc(r);
+			if (priv->cache_buf == NULL) {
+				return SC_ERROR_OUT_OF_MEMORY;
+			}
+			memcpy(priv->cache_buf, buffer, r);
+			priv->cache_buf_len = r;
+		}
+		priv->cached = 1;
+	}
+	if (offset >= priv->cache_buf_len) {
+		return 0;
+	}
+	size = (int) MIN((priv->cache_buf_len - offset), count);
+	memcpy(buf, priv->cache_buf + offset, size);
+	return size;
+}
+
+static int
+idprime_set_security_env(struct sc_card *card,
+	const struct sc_security_env *env, int se_num)
+{
+	int r;
+	struct sc_security_env new_env;
+
+	if (card == NULL || env == NULL) {
+		return SC_ERROR_INVALID_ARGUMENTS;
+	}
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	/* The card requires algorithm reference here */
+	new_env = *env;
+	new_env.flags |= SC_SEC_ENV_ALG_REF_PRESENT;
+	/* SHA-1 mechanisms are not allowed in the card I have available */
+	switch (env->operation) {
+	case SC_SEC_OPERATION_DECIPHER:
+		if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_OAEP) {
+			if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA1) {
+				new_env.algorithm_ref = 0x1D;
+			} else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA256) {
+				new_env.algorithm_ref = 0x4D;
+			} else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA384) {
+				new_env.algorithm_ref = 0x5D;
+			} else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA512) {
+				new_env.algorithm_ref = 0x6D;
+			}
+		} else { /* RSA-PKCS without hashing */
+			new_env.algorithm_ref = 0x1A;
+		}
+		break;
+	case SC_SEC_OPERATION_SIGN:
+		if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PSS) {
+			if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA256) {
+				new_env.algorithm_ref = 0x45;
+			} else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA384) {
+				new_env.algorithm_ref = 0x55;
+			} else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA512) {
+				new_env.algorithm_ref = 0x65;
+			}
+		} else { /* RSA-PKCS */
+			if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA256) {
+				new_env.algorithm_ref = 0x42;
+			} else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA384) {
+				new_env.algorithm_ref = 0x52;
+			} else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA512) {
+				new_env.algorithm_ref = 0x62;
+			} else { /* RSA-PKCS without hashing */
+				new_env.algorithm_ref = 0x02;
+			}
+		}
+		break;
+	default:
+		return SC_ERROR_INVALID_ARGUMENTS;
+	}
+	r = iso_ops->set_security_env(card,
+		(const struct sc_security_env *) &new_env, se_num);
+
+	LOG_FUNC_RETURN(card->ctx, r);
+}
+
+/* These are mostly ISO versions updated to IDPrime specifics */
+static int
+idprime_compute_signature(struct sc_card *card,
+	const u8 * data, size_t datalen, u8 * out, size_t outlen)
+{
+	int r;
+	struct sc_apdu apdu;
+	u8 *p;
+	u8 sbuf[128]; /* For SHA-512 we need 64 + 2 bytes */
+	u8 rbuf[4096]; /* needs work. for 3072 keys, needs 384+2 or so */
+	size_t rbuflen = sizeof(rbuf);
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	/* We should be signing hashes only so we should not reach this limit */
+	if (datalen + 2 > sizeof(sbuf)) {
+		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
+	}
+
+	p = sbuf;
+	*(p++) = 0x90;
+	*(p++) = datalen;
+	memcpy(p, data, datalen);
+	p += datalen;
+
+	/* INS: 0x2A  PERFORM SECURITY OPERATION
+	 * P1:  0x90  Hash code
+	 * P2:  0xA0  Input template for the computation of a hash-code (the template is hashed) */
+	sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x90, 0xA0);
+	apdu.resp = rbuf;
+	apdu.resplen = rbuflen;
+	apdu.le = datalen;
+
+	apdu.data = sbuf;
+	apdu.lc = p - sbuf;
+	apdu.datalen = p - sbuf;
+
+	r = sc_transmit_apdu(card, &apdu);
+	LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
+
+	/* This just returns the passed data (hash code) (for verification?) */
+	if (apdu.resplen != datalen || memcmp(rbuf, data, datalen) != 0) {
+		sc_log(card->ctx, "The initial APDU did not return the same data");
+		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
+	}
+	/* INS: 0x2A  PERFORM SECURITY OPERATION
+	 * P1:  0x9E  Resp: Digital Signature
+	 * P2:  0x9A  Cmd: Input for Digital Signature */
+	sc_format_apdu(card, &apdu, SC_APDU_CASE_2, 0x2A, 0x9E, 0x9A);
+	apdu.resp = out;
+	apdu.resplen = outlen;
+	apdu.le = outlen;
+	if (apdu.le > sc_get_max_recv_size(card)) {
+		/* The lower layers will automatically do a GET RESPONSE, if possible.
+		 * All other workarounds must be carried out by the upper layers. */
+		apdu.le = sc_get_max_recv_size(card);
+	}
+
+	apdu.data = NULL;
+	apdu.datalen = 0;
+	apdu.lc = 0;
+	r = sc_transmit_apdu(card, &apdu);
+	LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
+
+	if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
+		LOG_FUNC_RETURN(card->ctx, apdu.resplen);
+
+	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+	LOG_TEST_RET(card->ctx, r, "Card returned error");
+
+	LOG_FUNC_RETURN(card->ctx, r);
+}
+
+/* These are mostly ISO versions updated to IDPrime specifics */
+static int
+idprime_decipher(struct sc_card *card,
+	const u8 * crgram, size_t crgram_len,
+	u8 * out, size_t outlen)
+{
+	int r;
+	struct sc_apdu apdu;
+	u8 *sbuf = NULL;
+
+	if (card == NULL || crgram == NULL || out == NULL) {
+		return SC_ERROR_INVALID_ARGUMENTS;
+	}
+	LOG_FUNC_CALLED(card->ctx);
+	sc_log(card->ctx,
+		"IDPrime decipher: in-len %"SC_FORMAT_LEN_SIZE_T"u, out-len %"SC_FORMAT_LEN_SIZE_T"u",
+		crgram_len, outlen);
+
+	sbuf = malloc(crgram_len + 1);
+	if (sbuf == NULL)
+		return SC_ERROR_OUT_OF_MEMORY;
+
+	/* 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, 0x2A, 0x80, 0x86);
+	apdu.resp    = out;
+	apdu.resplen = outlen;
+	apdu.le      = outlen;
+
+	sbuf[0] = 0x81; /* padding indicator byte, 0x81 = Proprietary */
+	memcpy(sbuf + 1, crgram, crgram_len);
+	apdu.data = sbuf;
+	apdu.lc = crgram_len + 1;
+	if (apdu.lc > sc_get_max_send_size(card)) {
+		/* The lower layers will automatically do chaining */
+		apdu.flags |= SC_APDU_FLAGS_CHAINING;
+	}
+	if (apdu.le > sc_get_max_recv_size(card)) {
+		/* The lower layers will automatically do a GET RESPONSE, if possible.
+		 * All other workarounds must be carried out by the upper layers. */
+		apdu.le = sc_get_max_recv_size(card);
+	}
+	apdu.datalen = crgram_len + 1;
+
+	r = sc_transmit_apdu(card, &apdu);
+	sc_mem_clear(sbuf, crgram_len + 1);
+	free(sbuf);
+	LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
+
+	if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
+		LOG_FUNC_RETURN(card->ctx, apdu.resplen);
+	else
+		LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2));
+}
+
+
+static struct sc_card_driver * sc_get_driver(void)
+{
+	if (iso_ops == NULL) {
+		iso_ops = sc_get_iso7816_driver()->ops;
+	}
+
+	idprime_ops = *iso_ops;
+	idprime_ops.match_card = idprime_match_card;
+	idprime_ops.init = idprime_init;
+	idprime_ops.finish = idprime_finish;
+
+	idprime_ops.read_binary = idprime_read_binary;
+	idprime_ops.select_file = idprime_select_file;
+	idprime_ops.card_ctl = idprime_card_ctl;
+	idprime_ops.set_security_env = idprime_set_security_env;
+	idprime_ops.compute_signature = idprime_compute_signature;
+	idprime_ops.decipher = idprime_decipher;
+
+	return &idprime_drv;
+}
+
+struct sc_card_driver * sc_get_idprime_driver(void)
+{
+	return sc_get_driver();
+}
diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h
index ac1969256b..7bba7e6687 100644
--- a/src/libopensc/cardctl.h
+++ b/src/libopensc/cardctl.h
@@ -303,6 +303,16 @@ enum {
 	SC_CARDCTL_GIDS_INITIALIZE,
 	SC_CARDCTL_GIDS_SET_ADMIN_KEY,
 	SC_CARDCTL_GIDS_AUTHENTICATE_ADMIN,
+
+	/*
+	 * IDPrime specific calls
+	 */
+	SC_CARDCTL_IDPRIME_BASE = _CTL_PREFIX('I', 'D', 'P'),
+	SC_CARDCTL_IDPRIME_INIT_GET_OBJECTS,
+	SC_CARDCTL_IDPRIME_GET_NEXT_OBJECT,
+	SC_CARDCTL_IDPRIME_FINAL_GET_OBJECTS,
+	SC_CARDCTL_IDPRIME_GET_TOKEN_NAME,
+
 };
 
 enum {
diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h
index 24d73c094a..cb0501c3aa 100644
--- a/src/libopensc/cards.h
+++ b/src/libopensc/cards.h
@@ -234,6 +234,7 @@ enum {
 	/* JPKI cards */
 	SC_CARD_TYPE_JPKI_BASE = 31000,
 
+	/* Coolkey cards */
 	SC_CARD_TYPE_COOLKEY_BASE = 32000,
 	SC_CARD_TYPE_COOLKEY_GENERIC,
 
@@ -258,6 +259,12 @@ enum {
 	SC_CARD_TYPE_RUTOKEN_ECP_SC,
 	SC_CARD_TYPE_RUTOKEN_LITE,
 	SC_CARD_TYPE_RUTOKEN_LITE_SC,
+
+	/* IDPrime cards */
+	SC_CARD_TYPE_IDPRIME_BASE = 37000,
+	SC_CARD_TYPE_IDPRIME_V1,
+	SC_CARD_TYPE_IDPRIME_V2,
+	SC_CARD_TYPE_IDPRIME_GENERIC,
 };
 
 extern sc_card_driver_t *sc_get_default_driver(void);
@@ -301,6 +308,7 @@ extern sc_card_driver_t *sc_get_cac_driver(void);
 extern sc_card_driver_t *sc_get_cac1_driver(void);
 extern sc_card_driver_t *sc_get_npa_driver(void);
 extern sc_card_driver_t *sc_get_esteid2018_driver(void);
+extern sc_card_driver_t *sc_get_idprime_driver(void);
 
 #ifdef __cplusplus
 }
diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c
index 40f573ed0f..4c5adc6e5f 100644
--- a/src/libopensc/ctx.c
+++ b/src/libopensc/ctx.c
@@ -128,6 +128,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = {
 	{ "atrust-acos",(void *(*)(void)) sc_get_atrust_acos_driver },
 	{ "westcos",	(void *(*)(void)) sc_get_westcos_driver },
 	{ "esteid2018",	(void *(*)(void)) sc_get_esteid2018_driver },
+	{ "idprime",	(void *(*)(void)) sc_get_idprime_driver },
 
 /* Here should be placed drivers that need some APDU transactions in the
  * driver's `match_card()` function. */
diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h
index d8ec42174e..10bea0a013 100644
--- a/src/libopensc/opensc.h
+++ b/src/libopensc/opensc.h
@@ -107,12 +107,13 @@ extern "C" {
  * must support at least one of them, and exactly one of them must be selected
  * for a given operation. */
 #define SC_ALGORITHM_RSA_RAW		0x00000001
-#define SC_ALGORITHM_RSA_PADS		0x0000001F
+#define SC_ALGORITHM_RSA_PADS		0x0000003F
 #define SC_ALGORITHM_RSA_PAD_NONE	0x00000001
 #define SC_ALGORITHM_RSA_PAD_PKCS1	0x00000002 /* PKCS#1 v1.5 padding */
 #define SC_ALGORITHM_RSA_PAD_ANSI	0x00000004
 #define SC_ALGORITHM_RSA_PAD_ISO9796	0x00000008
 #define SC_ALGORITHM_RSA_PAD_PSS	0x00000010 /* PKCS#1 v2.0 PSS */
+#define SC_ALGORITHM_RSA_PAD_OAEP	0x00000020 /* PKCS#1 v2.0 OAEP */
 
 /* If the card is willing to produce a cryptogram with the following
  * hash values, set these flags accordingly.  The interpretation of the hash
diff --git a/src/libopensc/padding.c b/src/libopensc/padding.c
index 4a8fc17b9c..5bcb2bb8d9 100644
--- a/src/libopensc/padding.c
+++ b/src/libopensc/padding.c
@@ -499,6 +499,7 @@ int sc_get_encoding_flags(sc_context_t *ctx,
 		/* Use the card's raw RSA capability on the padded input */
 		*sflags = SC_ALGORITHM_RSA_PAD_NONE;
 		*pflags = iflags;
+		/* TODO emulate the OAEP decryption */
 
 	} else if ((caps & (SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE)) &&
 			(iflags & SC_ALGORITHM_RSA_PAD_PKCS1)) {
diff --git a/src/libopensc/pkcs15-cac.c b/src/libopensc/pkcs15-cac.c
index d637dedf19..e8c13d5fea 100644
--- a/src/libopensc/pkcs15-cac.c
+++ b/src/libopensc/pkcs15-cac.c
@@ -84,89 +84,6 @@ static const char * cac_get_name(int type)
     return ("CAC");
 }
 
-/*
- * These could move to a helper file for other cards that wish to use usage as a way of getting flags
- */
-
-/* Only certain usages are valid for a given algorithm, return all the usages that the algorithm supports so we
- * can use it as a filter for all the public and private key usages */
-static unsigned int
-cac_alg_flags_from_algorithm(int algorithm)
-{
-	switch (algorithm) {
-	case SC_ALGORITHM_RSA:
-		return SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP |
-		       SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER |
-		       SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP |
-		       SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER |
-		       SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
-	case SC_ALGORITHM_DSA:
-		return SC_PKCS15_PRKEY_USAGE_VERIFY| SC_PKCS15_PRKEY_USAGE_SIGN |
-		       SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
-#ifdef SC_ALGORITHM_DH
-	case SC_ALGORITHM_DH:
-		return SC_PKCS15_PRKEY_USAGE_DERIVE ;
-#endif
-	case SC_ALGORITHM_EC:
-		return SC_PKCS15_PRKEY_USAGE_DERIVE | SC_PKCS15_PRKEY_USAGE_VERIFY|
-		       SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
-	case SC_ALGORITHM_GOSTR3410:
-		return SC_PKCS15_PRKEY_USAGE_DERIVE | SC_PKCS15_PRKEY_USAGE_VERIFY|
-		       SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
-	}
-	return 0;
-}
-
-/* These are the cert key usage bits that map to various PKCS #11 (and thus PKCS #15) flags */
-#define CAC_X509_USAGE_SIGNATURE \
-	(SC_X509_DIGITAL_SIGNATURE | \
-	SC_X509_NON_REPUDIATION    | \
-	SC_X509_KEY_CERT_SIGN      | \
-	SC_X509_CRL_SIGN)
-#define CAC_X509_USAGE_DERIVE \
-	SC_X509_KEY_AGREEMENT
-#define CAC_X509_USAGE_UNWRAP \
-	(SC_X509_KEY_ENCIPHERMENT | \
-	SC_X509_KEY_AGREEMENT)
-#define CAC_X509_USAGE_DECRYPT \
-	(SC_X509_DATA_ENCIPHERMENT | \
-	SC_X509_ENCIPHER_ONLY)
-#define CAC_X509_USAGE_NONREPUDIATION \
-	SC_X509_NON_REPUDIATION
-
-/* map a cert usage and algorithm to public and private key usages */
-static int
-cac_map_usage(unsigned int cert_usage, int algorithm, unsigned int *pub_usage_ptr, unsigned int *pr_usage_ptr, int allow_nonrepudiation)
-{
-	unsigned int pub_usage = 0, pr_usage = 0;
-	unsigned int alg_flags = cac_alg_flags_from_algorithm(algorithm);
-
-	if (cert_usage & CAC_X509_USAGE_SIGNATURE) {
-		pub_usage |= SC_PKCS15_PRKEY_USAGE_VERIFY|SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER;
-		pr_usage |= SC_PKCS15_PRKEY_USAGE_SIGN|SC_PKCS15_PRKEY_USAGE_SIGNRECOVER;
-	}
-	if (cert_usage & CAC_X509_USAGE_DERIVE) {
-		pub_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE;
-		pr_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE;
-	}
-	if (cert_usage & (CAC_X509_USAGE_DECRYPT|CAC_X509_USAGE_UNWRAP)) {
-		pub_usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT;
-		pr_usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT;
-	}
-	if (allow_nonrepudiation && (cert_usage & CAC_X509_USAGE_NONREPUDIATION)) {
-		pub_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
-		pr_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
-	}
-	/* filter usages algorithm */
-	if (pub_usage_ptr) {
-		*pub_usage_ptr = pub_usage & alg_flags;
-	}
-	if (pr_usage_ptr) {
-		*pr_usage_ptr = pr_usage & alg_flags;
-	}
-	return SC_SUCCESS;
-}
-
 static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card)
 {
 	static const pindata pins[] = {
@@ -407,9 +324,9 @@ static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card)
 
 		r = sc_pkcs15_get_bitstring_extension(card->ctx, cert_out, &usage_type, &usage, NULL);
 		if (r < 0) {
-			usage = 0xd9ULL; /* basic default usage */
+			usage = SC_X509_DATA_ENCIPHERMENT|SC_X509_DIGITAL_SIGNATURE; /* basic default usage */
 		}
-		cac_map_usage(usage, cert_out->key->algorithm, &pubkey_info.usage, &prkey_info.usage, 1);
+		sc_pkcs15_map_usage(usage, cert_out->key->algorithm, &pubkey_info.usage, &prkey_info.usage, 1);
 		sc_log(card->ctx,   "cert %s: cert_usage=0x%x, pub_usage=0x%x priv_usage=0x%x\n",
 				sc_dump_hex(cert_info.id.value, cert_info.id.len),
 				 usage, pubkey_info.usage, prkey_info.usage);
diff --git a/src/libopensc/pkcs15-cert.c b/src/libopensc/pkcs15-cert.c
index 8606d14af0..7850fad8d8 100644
--- a/src/libopensc/pkcs15-cert.c
+++ b/src/libopensc/pkcs15-cert.c
@@ -257,6 +257,8 @@ sc_pkcs15_get_extension(struct sc_context *ctx, struct sc_pkcs15_cert *cert,
 		{ NULL, 0, 0, 0, NULL, NULL }
 	};
 
+	LOG_FUNC_CALLED(ctx);
+
 	for (next_ext = cert->extensions, next_ext_len = cert->extensions_len; next_ext_len; ) {
 		/* unwrap the set and point to the next ava */
 		ext = sc_asn1_skip_tag(ctx, &next_ext, &next_ext_len,
@@ -324,6 +326,8 @@ sc_pkcs15_get_bitstring_extension(struct sc_context *ctx,
 		{ NULL, 0, 0, 0, NULL, NULL }
 	};
 
+	LOG_FUNC_CALLED(ctx);
+
 	r = sc_pkcs15_get_extension(ctx, cert, type, &bit_string, &bit_string_len, is_critical);
 	LOG_TEST_RET(ctx, r, "Get extension error");
 
@@ -550,6 +554,88 @@ sc_pkcs15_encode_cdf_entry(sc_context_t *ctx, const struct sc_pkcs15_object *obj
 	return r;
 }
 
+/* Only certain usages are valid for a given algorithm, return all the usages
+ * that the algorithm supports so we can use it as a filter for all
+ * the public and private key usages
+ */
+static unsigned int
+sc_pkcs15_alg_flags_from_algorithm(int algorithm)
+{
+	switch (algorithm) {
+	case SC_ALGORITHM_RSA:
+		return SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP |
+		       SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER |
+		       SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP |
+		       SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER |
+		       SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+	case SC_ALGORITHM_DSA:
+		return SC_PKCS15_PRKEY_USAGE_VERIFY| SC_PKCS15_PRKEY_USAGE_SIGN |
+		       SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+#ifdef SC_ALGORITHM_DH
+	case SC_ALGORITHM_DH:
+		return SC_PKCS15_PRKEY_USAGE_DERIVE ;
+#endif
+	case SC_ALGORITHM_EC:
+		return SC_PKCS15_PRKEY_USAGE_DERIVE | SC_PKCS15_PRKEY_USAGE_VERIFY|
+		       SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+	case SC_ALGORITHM_GOSTR3410:
+		return SC_PKCS15_PRKEY_USAGE_DERIVE | SC_PKCS15_PRKEY_USAGE_VERIFY|
+		       SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+	}
+	return 0;
+}
+
+/* These are the cert key usage bits that map to various PKCS #11 (and thus PKCS #15) flags */
+#define SC_PKCS15_X509_USAGE_SIGNATURE \
+	(SC_X509_DIGITAL_SIGNATURE | \
+	SC_X509_NON_REPUDIATION    | \
+	SC_X509_KEY_CERT_SIGN      | \
+	SC_X509_CRL_SIGN)
+#define SC_PKCS15_X509_USAGE_DERIVE \
+	SC_X509_KEY_AGREEMENT
+#define SC_PKCS15_X509_USAGE_UNWRAP \
+	(SC_X509_KEY_ENCIPHERMENT | \
+	SC_X509_KEY_AGREEMENT)
+#define SC_PKCS15_X509_USAGE_DECRYPT \
+	(SC_X509_DATA_ENCIPHERMENT | \
+	SC_X509_ENCIPHER_ONLY)
+#define SC_PKCS15_X509_USAGE_NONREPUDIATION \
+	SC_X509_NON_REPUDIATION
+
+/* map a cert usage and algorithm to public and private key usages */
+int
+sc_pkcs15_map_usage(unsigned int cert_usage, int algorithm,
+	unsigned int *pub_usage_ptr, unsigned int *pr_usage_ptr,
+	int allow_nonrepudiation)
+{
+	unsigned int pub_usage = 0, pr_usage = 0;
+	unsigned int alg_flags = sc_pkcs15_alg_flags_from_algorithm(algorithm);
+
+	if (cert_usage & SC_PKCS15_X509_USAGE_SIGNATURE) {
+		pub_usage |= SC_PKCS15_PRKEY_USAGE_VERIFY|SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER;
+		pr_usage |= SC_PKCS15_PRKEY_USAGE_SIGN|SC_PKCS15_PRKEY_USAGE_SIGNRECOVER;
+	}
+	if (cert_usage & SC_PKCS15_X509_USAGE_DERIVE) {
+		pub_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE;
+		pr_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE;
+	}
+	if (cert_usage & (SC_PKCS15_X509_USAGE_DECRYPT|SC_PKCS15_X509_USAGE_UNWRAP)) {
+		pub_usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT;
+		pr_usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT;
+	}
+	if (allow_nonrepudiation && (cert_usage & SC_PKCS15_X509_USAGE_NONREPUDIATION)) {
+		pub_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+		pr_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+	}
+	/* filter usages algorithm */
+	if (pub_usage_ptr) {
+		*pub_usage_ptr = pub_usage & alg_flags;
+	}
+	if (pr_usage_ptr) {
+		*pr_usage_ptr = pr_usage & alg_flags;
+	}
+	return SC_SUCCESS;
+}
 
 void
 sc_pkcs15_free_certificate(struct sc_pkcs15_cert *cert)
diff --git a/src/libopensc/pkcs15-idprime.c b/src/libopensc/pkcs15-idprime.c
new file mode 100644
index 0000000000..d882696997
--- /dev/null
+++ b/src/libopensc/pkcs15-idprime.c
@@ -0,0 +1,286 @@
+/*
+ * partial PKCS15 emulation for IDPrime cards.
+ *
+ * We can not use the ISO code, since the EF.DIR and EF.ATR for
+ * object discovery are missing
+ *
+ * Copyright (C) 2019, Red Hat, Inc.
+ *
+ * Author: Jakub Jelen <jjelen@redhat.com>
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include "internal.h"
+#include "cardctl.h"
+#include "pkcs15.h"
+
+#define CERT_LABEL_TEMPLATE "Certificate %d"
+#define PUBKEY_LABEL_TEMPLATE "Public key %d"
+#define PRIVKEY_LABEL_TEMPLATE "Private key %d"
+
+static int idprime_detect_card(sc_pkcs15_card_t *p15card)
+{
+	sc_card_t *card = p15card->card;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	if (card->type < SC_CARD_TYPE_IDPRIME_BASE
+		|| card->type >= SC_CARD_TYPE_IDPRIME_BASE+1000)
+		return SC_ERROR_INVALID_CARD;
+	return SC_SUCCESS;
+}
+
+static int sc_pkcs15emu_idprime_init(sc_pkcs15_card_t *p15card)
+{
+	int r, i;
+	sc_card_t *card = p15card->card;
+	sc_serial_number_t serial;
+	char buf[SC_MAX_SERIALNR * 2 + 1];
+	int count;
+	char *token_name = NULL;
+	struct sc_pkcs15_auth_info pin_info;
+	struct sc_pkcs15_object   pin_obj;
+	const char pin_label[] = "PIN";
+	const char *pin_id = "11";
+
+	/* oid for key usage */
+	static const struct sc_object_id usage_type = {{ 2, 5, 29, 15, -1 }};
+	unsigned int usage;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	/* could read this off card if needed */
+	p15card->tokeninfo->label = strdup("IDPrime");
+	p15card->tokeninfo->manufacturer_id = strdup("Gemalto");
+
+	/*
+	 * get serial number
+	 */
+	memset(&serial, 0, sizeof(serial));
+	r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial);
+	if (r < 0) {
+		sc_log(card->ctx, "sc_card_ctl rc=%d", r);
+		p15card->tokeninfo->serial_number = strdup("00000000");
+	} else {
+		sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0);
+		p15card->tokeninfo->serial_number = strdup(buf);
+	}
+	/* set pin */
+	sc_log(card->ctx,  "IDPrime adding pin...");
+	memset(&pin_info, 0, sizeof(pin_info));
+	memset(&pin_obj,  0, sizeof(pin_obj));
+
+	pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;
+	sc_pkcs15_format_id(pin_id, &pin_info.auth_id);
+	pin_info.attrs.pin.reference     = 0x11;
+	pin_info.attrs.pin.flags         = SC_PKCS15_PIN_FLAG_INITIALIZED;
+	pin_info.attrs.pin.type          = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC;
+	pin_info.attrs.pin.min_length    = 4;
+	pin_info.attrs.pin.stored_length = 0;
+	pin_info.attrs.pin.max_length    = 16;
+	pin_info.tries_left    = -1;
+
+	sc_log(card->ctx,  "IDPrime Adding pin with label=%s", pin_label);
+	strncpy(pin_obj.label, pin_label, SC_PKCS15_MAX_LABEL_SIZE - 1);
+	pin_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE;
+
+	r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info);
+	if (r < 0)
+		LOG_FUNC_RETURN(card->ctx, r);
+
+	/*
+	 * get token name if provided
+	 */
+	r = sc_card_ctl(card, SC_CARDCTL_IDPRIME_GET_TOKEN_NAME, &token_name);
+	if (r < 0) {
+		/* On failure we will get the token name from certificates later */
+		sc_log(card->ctx, "sc_card_ctl rc=%d", r);
+	} else {
+		free(p15card->tokeninfo->label);
+		p15card->tokeninfo->label = token_name;
+		sc_log(card->ctx,  "IDPrime setting token label = %s", token_name);
+	}
+	/*
+	 * certs, pubkeys and priv keys are related and we assume
+	 * they are in order
+	 * We need to read the cert, get modulus and keylen
+	 * We use those for the pubkey, and priv key objects.
+	 */
+	sc_log(card->ctx,  "IDPrime adding certs, pub and priv keys...");
+	r = (card->ops->card_ctl)(card, SC_CARDCTL_IDPRIME_INIT_GET_OBJECTS, &count);
+	LOG_TEST_RET(card->ctx, r, "Can not initiate cert objects.");
+
+	for (i = 0; i < count; i++) {
+		struct sc_pkcs15_prkey_info prkey_info;
+		struct sc_pkcs15_cert_info cert_info;
+		struct sc_pkcs15_pubkey_info pubkey_info;
+		struct sc_pkcs15_object cert_obj;
+		struct sc_pkcs15_object pubkey_obj;
+		struct sc_pkcs15_object prkey_obj;
+		sc_pkcs15_der_t cert_der;
+		sc_pkcs15_cert_t *cert_out = NULL;
+
+		r = (card->ops->card_ctl)(card, SC_CARDCTL_IDPRIME_GET_NEXT_OBJECT, &prkey_info);
+		LOG_TEST_RET(card->ctx, r, "Can not get next object");
+
+		memset(&cert_info, 0, sizeof(cert_info));
+		memset(&pubkey_info, 0, sizeof(pubkey_info));
+		/* prkey_info cleaned by the card_ctl call */
+		memset(&cert_obj,  0, sizeof(cert_obj));
+		memset(&pubkey_obj,  0, sizeof(pubkey_obj));
+		memset(&prkey_obj,  0, sizeof(prkey_obj));
+
+		cert_info.id = prkey_info.id;
+		pubkey_info.id = prkey_info.id;
+		cert_info.path = prkey_info.path;
+		/* For private keys, we no longer care for the path, just
+		 * the key reference later used in the security environment */
+		prkey_info.path.len = 0;
+		prkey_info.path.aid.len = 0;
+		pubkey_info.key_reference = prkey_info.key_reference;
+		sc_log(card->ctx,  "Key ref r=%x", prkey_info.key_reference);
+
+		pubkey_info.native        = 1;
+		prkey_info.native        = 1;
+
+		snprintf(cert_obj.label, SC_PKCS15_MAX_LABEL_SIZE, CERT_LABEL_TEMPLATE, i+1);
+		snprintf(pubkey_obj.label, SC_PKCS15_MAX_LABEL_SIZE, PUBKEY_LABEL_TEMPLATE, i+1);
+		snprintf(prkey_obj.label, SC_PKCS15_MAX_LABEL_SIZE, PRIVKEY_LABEL_TEMPLATE, i+1);
+		prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE;
+		sc_pkcs15_format_id(pin_id, &prkey_obj.auth_id);
+
+		r = sc_pkcs15_read_file(p15card, &cert_info.path, &cert_der.value, &cert_der.len);
+
+		if (r) {
+			sc_log(card->ctx,  "No cert found,i=%d", i);
+			continue;
+		}
+		cert_info.path.count = cert_der.len;
+
+		sc_log(card->ctx,
+			 "cert len=%"SC_FORMAT_LEN_SIZE_T"u, cert_info.path.count=%d r=%d\n",
+			 cert_der.len, cert_info.path.count, r);
+		sc_log_hex(card->ctx, "cert", cert_der.value, cert_der.len);
+
+		/* cache it using the PKCS15 emulation objects */
+		/* as it does not change */
+		if (cert_der.value) {
+			cert_info.value.value = cert_der.value;
+			cert_info.value.len = cert_der.len;
+			cert_info.path.len = 0; /* use in mem cert from now on */
+		}
+
+		/* following will find the cached cert in cert_info */
+		r =  sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out);
+		if (r < 0 || cert_out->key == NULL) {
+			sc_log(card->ctx,  "Failed to read/parse the certificate r=%d",r);
+			if (cert_out != NULL)
+				sc_pkcs15_free_certificate(cert_out);
+			free(cert_der.value);
+			continue;
+		}
+
+		r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
+		if (r < 0) {
+			sc_log(card->ctx,  " Failed to add cert obj r=%d",r);
+			sc_pkcs15_free_certificate(cert_out);
+			free(cert_der.value);
+			continue;
+		}
+		/* set the token name to the name of the CN of the first certificate */
+		if (!token_name) {
+			u8 * cn_name = NULL;
+			size_t cn_len = 0;
+			static const struct sc_object_id cn_oid = {{ 2, 5, 4, 3, -1 }};
+			r = sc_pkcs15_get_name_from_dn(card->ctx, cert_out->subject,
+				cert_out->subject_len, &cn_oid, &cn_name, &cn_len);
+			if (r == SC_SUCCESS) {
+				token_name = malloc (cn_len+1);
+				if (!token_name) {
+					free(cn_name);
+					r = SC_ERROR_OUT_OF_MEMORY;
+					goto fail;
+				}
+				memcpy(token_name, cn_name, cn_len);
+				free(cn_name);
+				token_name[cn_len] = '\0';
+				free(p15card->tokeninfo->label);
+				p15card->tokeninfo->label = token_name;
+			}
+		}
+
+
+		r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, cert_out->key,
+			&pubkey_info.direct.spki.value, &pubkey_info.direct.spki.len);
+		if (r < 0)
+			goto fail;
+		pubkey_obj.emulated = cert_out->key;
+
+		r = sc_pkcs15_get_bitstring_extension(card->ctx, cert_out, &usage_type, &usage, NULL);
+		if (r < 0) {
+			usage = SC_X509_DATA_ENCIPHERMENT|SC_X509_DIGITAL_SIGNATURE; /* basic default usage */
+		}
+		sc_pkcs15_map_usage(usage, cert_out->key->algorithm, &pubkey_info.usage, &prkey_info.usage, 1);
+		sc_log(card->ctx, "cert %s: cert_usage=0x%x, pub_usage=0x%x priv_usage=0x%x\n",
+			sc_dump_hex(cert_info.id.value, cert_info.id.len),
+			usage, pubkey_info.usage, prkey_info.usage);
+		if (cert_out->key->algorithm != SC_ALGORITHM_RSA) {
+			sc_log(card->ctx, "unsupported key.algorithm %d", cert_out->key->algorithm);
+			sc_pkcs15_free_certificate(cert_out);
+			continue;
+		} else {
+			pubkey_info.modulus_length = cert_out->key->u.rsa.modulus.len * 8;
+			prkey_info.modulus_length = cert_out->key->u.rsa.modulus.len * 8;
+			sc_log(card->ctx,  "adding rsa public key r=%d usage=%x",r, pubkey_info.usage);
+			r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info);
+			if (r < 0)
+				goto fail;
+			sc_log(card->ctx,  "adding rsa private key r=%d usage=%x",r, prkey_info.usage);
+			r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info);
+			if (r < 0)
+				goto fail;
+		}
+
+		cert_out->key = NULL;
+fail:
+		sc_pkcs15_free_certificate(cert_out);
+		if (r < 0)
+			LOG_FUNC_RETURN(card->ctx, r); /* should not fail */
+
+	}
+	r = (card->ops->card_ctl)(card, SC_CARDCTL_IDPRIME_FINAL_GET_OBJECTS, &count);
+	LOG_TEST_RET(card->ctx, r, "Can not finalize cert objects.");
+
+	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
+}
+
+int sc_pkcs15emu_idprime_init_ex(sc_pkcs15_card_t *p15card,
+		struct sc_aid *aid)
+{
+	sc_card_t   *card = p15card->card;
+	sc_context_t    *ctx = card->ctx;
+
+	LOG_FUNC_CALLED(ctx);
+
+	if (idprime_detect_card(p15card))
+		return SC_ERROR_WRONG_CARD;
+	return sc_pkcs15emu_idprime_init(p15card);
+}
diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c
index facb0a607c..279a001e9d 100644
--- a/src/libopensc/pkcs15-syn.c
+++ b/src/libopensc/pkcs15-syn.c
@@ -43,22 +43,23 @@ struct sc_pkcs15_emulator_handler builtin_emulators[] = {
 	{ "itacns",	sc_pkcs15emu_itacns_init_ex	},
 	{ "PIV-II",     sc_pkcs15emu_piv_init_ex	},
 	{ "cac",        sc_pkcs15emu_cac_init_ex	},
+	{ "idprime",    sc_pkcs15emu_idprime_init_ex	},
 	{ "gemsafeGPK",	sc_pkcs15emu_gemsafeGPK_init_ex	},
 	{ "gemsafeV1",	sc_pkcs15emu_gemsafeV1_init_ex	},
 	{ "actalis",	sc_pkcs15emu_actalis_init_ex	},
 	{ "atrust-acos",sc_pkcs15emu_atrust_acos_init_ex},
 	{ "tccardos",	sc_pkcs15emu_tccardos_init_ex	},
-	{ "entersafe",  sc_pkcs15emu_entersafe_init_ex  },
+	{ "entersafe",  sc_pkcs15emu_entersafe_init_ex	},
 	{ "pteid",	sc_pkcs15emu_pteid_init_ex	},
 	{ "oberthur",   sc_pkcs15emu_oberthur_init_ex	},
 	{ "sc-hsm",	sc_pkcs15emu_sc_hsm_init_ex	},
-	{ "dnie",       sc_pkcs15emu_dnie_init_ex   },
-	{ "gids",       sc_pkcs15emu_gids_init_ex   },
-	{ "iasecc",	sc_pkcs15emu_iasecc_init_ex   },
-	{ "jpki",	sc_pkcs15emu_jpki_init_ex },
+	{ "dnie",       sc_pkcs15emu_dnie_init_ex	},
+	{ "gids",       sc_pkcs15emu_gids_init_ex	},
+	{ "iasecc",	sc_pkcs15emu_iasecc_init_ex	},
+	{ "jpki",	sc_pkcs15emu_jpki_init_ex	},
 	{ "coolkey",    sc_pkcs15emu_coolkey_init_ex	},
-	{ "din66291",    sc_pkcs15emu_din_66291_init_ex	},
-	{ "esteid2018",    sc_pkcs15emu_esteid2018_init_ex	},
+	{ "din66291",   sc_pkcs15emu_din_66291_init_ex	},
+	{ "esteid2018", sc_pkcs15emu_esteid2018_init_ex	},
 
 	{ NULL, NULL }
 };
diff --git a/src/libopensc/pkcs15-syn.h b/src/libopensc/pkcs15-syn.h
index 6811b3dab1..ccaf693ca4 100644
--- a/src/libopensc/pkcs15-syn.h
+++ b/src/libopensc/pkcs15-syn.h
@@ -53,6 +53,7 @@ int sc_pkcs15emu_iasecc_init_ex(sc_pkcs15_card_t *,	struct sc_aid *);
 int sc_pkcs15emu_jpki_init_ex(sc_pkcs15_card_t *,	struct sc_aid *);
 int sc_pkcs15emu_coolkey_init_ex(sc_pkcs15_card_t *p15card,	struct sc_aid *);
 int sc_pkcs15emu_din_66291_init_ex(sc_pkcs15_card_t *p15card,	struct sc_aid *);
+int sc_pkcs15emu_idprime_init_ex(sc_pkcs15_card_t *p15card,	struct sc_aid *);
 
 struct sc_pkcs15_emulator_handler {
 	const char *name;
diff --git a/src/libopensc/pkcs15.h b/src/libopensc/pkcs15.h
index f2ccc42aed..df1435ce92 100644
--- a/src/libopensc/pkcs15.h
+++ b/src/libopensc/pkcs15.h
@@ -754,6 +754,9 @@ int sc_pkcs15_get_name_from_dn(struct sc_context *ctx,
                               const u8 *dn, size_t dn_len,
                               const struct sc_object_id *type,
                               u8 **name, size_t *name_len);
+int sc_pkcs15_map_usage(unsigned int cert_usage, int algorithm,
+			unsigned int *pub_usage_ptr, unsigned int *pr_usage_ptr,
+			int allow_nonrepudiation);
 int sc_pkcs15_get_extension(struct sc_context *ctx,
                             struct sc_pkcs15_cert *cert,
                             const struct sc_object_id *type,
diff --git a/src/libopensc/simpletlv.h b/src/libopensc/simpletlv.h
index a952779733..6bcd4f0856 100644
--- a/src/libopensc/simpletlv.h
+++ b/src/libopensc/simpletlv.h
@@ -29,7 +29,6 @@ extern "C" {
 #endif
 
 #include "libopensc/opensc.h"
-#include "libopensc/pkcs15.h"
 
 /*
  * Create a tag/length file in Simple TLV based on the  val_len  content length
diff --git a/src/pkcs11/framework-pkcs15.c b/src/pkcs11/framework-pkcs15.c
index 22081ffef1..e94eeeffec 100644
--- a/src/pkcs11/framework-pkcs15.c
+++ b/src/pkcs11/framework-pkcs15.c
@@ -3976,7 +3976,7 @@ pkcs15_prkey_sign(struct sc_pkcs11_session *session, void *obj,
 		/* Check the data length matches the selected hash */
 		rv = pkcs15_prkey_check_pss_param(pMechanism, (int)ulDataLen);
 		if (rv != CKR_OK) {
-			sc_log(context, "Invalid data lenght for the selected "
+			sc_log(context, "Invalid data length for the selected "
 			    "PSS parameters");
 			return rv;
 		}
@@ -4179,6 +4179,39 @@ pkcs15_prkey_decrypt(struct sc_pkcs11_session *session, void *obj,
 	case CKM_RSA_X_509:
 		flags |= SC_ALGORITHM_RSA_RAW;
 		break;
+	case CKM_RSA_PKCS_OAEP:
+		flags |= SC_ALGORITHM_RSA_PAD_OAEP;
+
+		/* Omited parameter can use MGF1-SHA1 and SHA1 hash ? */
+		if (pMechanism->pParameter == NULL) {
+			flags |= SC_ALGORITHM_RSA_HASH_SHA1;
+			flags |= SC_ALGORITHM_MGF1_SHA1;
+			break;
+		}
+
+		switch (((CK_RSA_PKCS_OAEP_PARAMS*)pMechanism->pParameter)->hashAlg) {
+		case CKM_SHA_1:
+			flags |= SC_ALGORITHM_RSA_HASH_SHA1;
+			break;
+		case CKM_SHA224:
+			flags |= SC_ALGORITHM_RSA_HASH_SHA224;
+			break;
+		case CKM_SHA256:
+			flags |= SC_ALGORITHM_RSA_HASH_SHA256;
+			break;
+		case CKM_SHA384:
+			flags |= SC_ALGORITHM_RSA_HASH_SHA384;
+			break;
+		case CKM_SHA512:
+			flags |= SC_ALGORITHM_RSA_HASH_SHA512;
+			break;
+		default:
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
+
+		/* The MGF parameter was already verified in SignInit() */
+		flags |= mgf2flags(((CK_RSA_PKCS_OAEP_PARAMS*)pMechanism->pParameter)->mgf);
+		break;
 	default:
 		return CKR_MECHANISM_INVALID;
 	}
@@ -4352,6 +4385,7 @@ pkcs15_prkey_init_params(struct sc_pkcs11_session *session,
 	const unsigned int salt_lens[5] = { 160, 256, 384, 512, 224 };
 	const unsigned int hashes[5] = { CKM_SHA_1, CKM_SHA256,
 		CKM_SHA384, CKM_SHA512, CKM_SHA224 };
+	const CK_RSA_PKCS_OAEP_PARAMS *oaep_params;
 
 	switch (pMechanism->mechanism) {
 	case CKM_RSA_PKCS_PSS:
@@ -4407,6 +4441,26 @@ pkcs15_prkey_init_params(struct sc_pkcs11_session *session,
 
 		/* TODO support different salt lengths */
 		break;
+	case CKM_RSA_PKCS_OAEP:
+		if (!pMechanism->pParameter ||
+			pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_OAEP_PARAMS))
+			return CKR_MECHANISM_PARAM_INVALID;
+
+		oaep_params = (CK_RSA_PKCS_OAEP_PARAMS*)pMechanism->pParameter;
+		switch (oaep_params->mgf) {
+		case CKG_MGF1_SHA1:
+		case CKG_MGF1_SHA224:
+		case CKG_MGF1_SHA256:
+		case CKG_MGF1_SHA384:
+		case CKG_MGF1_SHA512:
+			/* OK */
+			break;
+		default:
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
+		/* TODO support different salt lengths */
+		/* TODO is there something more to check */
+		break;
 	}
 	return CKR_OK;
 }
@@ -5619,6 +5673,7 @@ register_mechanisms(struct sc_pkcs11_card *p11card)
 		rsa_flags |= SC_ALGORITHM_RSA_PAD_PKCS1;
 #ifdef ENABLE_OPENSSL
 		rsa_flags |= SC_ALGORITHM_RSA_PAD_PSS;
+		/* TODO support OAEP decryption & encryption using OpenSSL */
 #endif
 	}
 
@@ -5699,6 +5754,7 @@ register_mechanisms(struct sc_pkcs11_card *p11card)
 	}
 
 	if (rsa_flags & SC_ALGORITHM_RSA_PAD_PSS) {
+		CK_FLAGS old_flags = mech_info.flags;
 		mech_info.flags &= ~(CKF_DECRYPT|CKF_ENCRYPT);
 		mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_PKCS_PSS, &mech_info, CKK_RSA, NULL, NULL);
 		rc = sc_pkcs11_register_mechanism(p11card, mt);
@@ -5735,6 +5791,18 @@ register_mechanisms(struct sc_pkcs11_card *p11card)
 			if (rc != CKR_OK)
 				return rc;
 		}
+		mech_info.flags = old_flags;
+	}
+
+	if (rsa_flags & SC_ALGORITHM_RSA_PAD_OAEP) {
+		CK_FLAGS old_flags = mech_info.flags;
+		mech_info.flags &= ~(CKF_SIGN|CKF_VERIFY|CKF_SIGN_RECOVER|CKF_VERIFY_RECOVER);
+		mt = sc_pkcs11_new_fw_mechanism(CKM_RSA_PKCS_OAEP, &mech_info, CKK_RSA, NULL, NULL);
+		rc = sc_pkcs11_register_mechanism(p11card, mt);
+		if (rc != CKR_OK) {
+			return rc;
+		}
+		mech_info.flags = old_flags;
 	}
 
 	if (rsa_flags & SC_ALGORITHM_ONBOARD_KEY_GEN) {
diff --git a/src/pkcs11/mechanism.c b/src/pkcs11/mechanism.c
index 358cad40fd..983d8dcbdf 100644
--- a/src/pkcs11/mechanism.c
+++ b/src/pkcs11/mechanism.c
@@ -811,6 +811,15 @@ sc_pkcs11_decr_init(struct sc_pkcs11_session *session,
 	}
 	rv = mt->decrypt_init(operation, key);
 
+	/* Validate the mechanism parameters */
+	if (key->ops->init_params) {
+		rv = key->ops->init_params(operation->session, &operation->mechanism);
+		if (rv != CKR_OK) {
+			/* Probably bad arguments */
+			LOG_FUNC_RETURN(context, (int) rv);
+		}
+	}
+
 	if (rv != CKR_OK)
 		session_stop_operation(session, SC_PKCS11_OPERATION_DECRYPT);
 
diff --git a/src/tests/p11test/p11test_case_common.c b/src/tests/p11test/p11test_case_common.c
index bd4b39af74..22e639cf49 100644
--- a/src/tests/p11test/p11test_case_common.c
+++ b/src/tests/p11test/p11test_case_common.c
@@ -422,7 +422,7 @@ int search_objects(test_certs_t *objects, token_info_t *info,
 	CK_OBJECT_HANDLE object_handle = CK_INVALID_HANDLE;
 	CK_OBJECT_HANDLE_PTR object_handles = NULL;
 	unsigned long i = 0, objects_length = 0;
-	int j;
+	int j, ret = -1;
 
 	/* FindObjects first
 	 * https://wiki.oasis-open.org/pkcs11/CommonBugs
@@ -439,16 +439,18 @@ int search_objects(test_certs_t *objects, token_info_t *info,
 			break;
 		if (rv != CKR_OK) {
 			fprintf(stderr, "C_FindObjects: rv = 0x%.8lX\n", rv);
-			return -1;
+			goto out;
 		}
 		/* store handle */
 		if (i >= objects_length) {
+			CK_OBJECT_HANDLE_PTR new_object_handles = NULL;
 			objects_length += 4; // do not realloc after each row
-			object_handles = realloc(object_handles, objects_length * sizeof(CK_OBJECT_HANDLE));
-			if (object_handles == NULL) {
+			new_object_handles = realloc(object_handles, objects_length * sizeof(CK_OBJECT_HANDLE));
+			if (new_object_handles == NULL) {
 		 		fail_msg("Realloc failed. Need to store object handles.\n");
-				return -1;
+				goto out;
 			}
+			object_handles = new_object_handles;
 		}
 		object_handles[i++] = object_handle;
 	}
@@ -458,8 +460,7 @@ int search_objects(test_certs_t *objects, token_info_t *info,
 	if (rv != CKR_OK) {
 		fprintf(stderr, "C_FindObjectsFinal: rv = 0x%.8lX\n", rv);
  		fail_msg("Could not find certificate.\n");
-		free(object_handles);
-		return -1;
+		goto out;
 	}
 
 	for (i = 0; i < objects_length; i++) {
@@ -476,8 +477,7 @@ int search_objects(test_certs_t *objects, token_info_t *info,
 				continue;
 			} else if (rv != CKR_OK) {
 				fail_msg("C_GetAttributeValue: rv = 0x%.8lX\n", rv);
-				free(object_handles);
-				return -1;
+				goto out;
 			}
 
 			/* Allocate memory to hold the data we want */
@@ -487,8 +487,7 @@ int search_objects(test_certs_t *objects, token_info_t *info,
 				template[j].pValue = malloc(template[j].ulValueLen);
 				if (template[j].pValue == NULL) {
 					fail_msg("malloc failed");
-					free(object_handles);
-					return -1;
+					goto out;
 				}
 			}
 			/* Call again to get actual attribute */
@@ -496,8 +495,7 @@ int search_objects(test_certs_t *objects, token_info_t *info,
 				&(template[j]), 1);
 			if (rv != CKR_OK) {
 				fail_msg("C_GetAttributeValue: rv = 0x%.8lX\n", rv);
-				free(object_handles);
-				return -1;
+				goto out;
 			}
 		}
 
@@ -506,8 +504,10 @@ int search_objects(test_certs_t *objects, token_info_t *info,
 		for (j = 0; j < template_size; j++)
 			free(template[j].pValue);
 	}
+	ret = 0;
+out:
 	free(object_handles);
-	return 0;
+	return ret;
 }
 
 void search_for_all_objects(test_certs_t *objects, token_info_t *info)
diff --git a/src/tools/opensc-tool.c b/src/tools/opensc-tool.c
index f829332fd8..52b570d9bf 100644
--- a/src/tools/opensc-tool.c
+++ b/src/tools/opensc-tool.c
@@ -594,6 +594,7 @@ static int list_algorithms(void)
 		{ SC_ALGORITHM_RSA_PAD_PKCS1,      "pkcs1"     },
 		{ SC_ALGORITHM_RSA_PAD_ANSI,       "ansi"      },
 		{ SC_ALGORITHM_RSA_PAD_PSS,        "pss"       },
+		{ SC_ALGORITHM_RSA_PAD_OAEP,       "oaep"      },
 		{ SC_ALGORITHM_RSA_PAD_ISO9796,    "iso9796"   },
 		{ SC_ALGORITHM_RSA_HASH_SHA1,      "sha1"      },
 		{ SC_ALGORITHM_RSA_HASH_MD5,       "MD5"       },
diff --git a/src/tools/pkcs11-tool.c b/src/tools/pkcs11-tool.c
index 92c8d8a506..6a8e27ee69 100644
--- a/src/tools/pkcs11-tool.c
+++ b/src/tools/pkcs11-tool.c
@@ -2127,9 +2127,15 @@ static void decrypt_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session,
 	case CKM_RSA_PKCS_OAEP:
 		oaep_params.hashAlg = opt_hash_alg;
 		switch (opt_hash_alg) {
+		case CKM_SHA_1:
+			oaep_params.mgf = CKG_MGF1_SHA1;
+			break;
 		case CKM_SHA224:
 			oaep_params.mgf = CKG_MGF1_SHA224;
 			break;
+		default:
+			oaep_params.hashAlg = CKM_SHA256;
+			/* fall through */
 		case CKM_SHA256:
 			oaep_params.mgf = CKG_MGF1_SHA256;
 			break;
@@ -2139,12 +2145,6 @@ static void decrypt_data(CK_SLOT_ID slot, CK_SESSION_HANDLE session,
 		case CKM_SHA512:
 			oaep_params.mgf = CKG_MGF1_SHA512;
 			break;
-		default:
-			oaep_params.hashAlg = CKM_SHA_1;
-			/* fall through */
-		case CKM_SHA_1:
-			oaep_params.mgf = CKG_MGF1_SHA1;
-			break;
 		}
 		break;
 	case CKM_RSA_X_509: