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-idprime.c b/src/libopensc/card-idprime.c new file mode 100644 index 0000000000..e2c7c61625 --- /dev/null +++ b/src/libopensc/card-idprime.c @@ -0,0 +1,687 @@ +/* + * 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]; +} 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 */ +} 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) { + sc_file_free(file); + LOG_FUNC_RETURN(card->ctx, r); + } + r = file->size; + sc_file_free(file); + 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]; + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "df=%s", + sc_dump_hex(new_object.df, sizeof(new_object.df))); + /* I assume this is some identification of certificate */ + if (start[4] == 0x6B && start[5] == 0x78 && start[6] == 0x63 + && start[7] == 0x30) { + new_object.fd++; + /* 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", + new_object.fd); + idprime_add_object_to_list(&priv->pki_list, &new_object); + } + } + r = SC_SUCCESS; +done: + free(buf); + LOG_FUNC_RETURN(card->ctx, r); +} + +static int idprime_init(sc_card_t *card) +{ + int r; + unsigned long flags; + idprime_private_data_t *priv = NULL; + + 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->type = SC_CARD_TYPE_IDPRIME_GENERIC; + card->drv_data = priv; + + card->name = "Gemalto IDPrime"; + 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_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_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..bd9833e501 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -303,6 +303,15 @@ 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, + }; enum { diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index 24d73c094a..f13f57eff1 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,10 @@ 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_GENERIC, }; extern sc_card_driver_t *sc_get_default_driver(void); @@ -301,6 +306,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/pkcs15-idprime.c b/src/libopensc/pkcs15-idprime.c new file mode 100644 index 0000000000..c28b3de823 --- /dev/null +++ b/src/libopensc/pkcs15-idprime.c @@ -0,0 +1,274 @@ +/* + * 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 = 8; + pin_info.attrs.pin.max_length = 8; + 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); + + /* + * 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;