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 + * + * 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 +#include +#include +#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 + * 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 +#include +#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: