2019-08-12 13:26:18 +00:00
|
|
|
/*
|
|
|
|
* card-idprime.c: Support for Gemalto IDPrime smart cards
|
|
|
|
*
|
|
|
|
* Copyright (c) 2019 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* Author: Jakub Jelen <jjelen@redhat.com>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#ifdef ENABLE_ZLIB
|
|
|
|
#include "compression.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "cardctl.h"
|
|
|
|
#include "pkcs15.h"
|
|
|
|
|
|
|
|
static const struct sc_card_operations *iso_ops = NULL;
|
|
|
|
|
|
|
|
static struct sc_card_operations idprime_ops;
|
|
|
|
static struct sc_card_driver idprime_drv = {
|
|
|
|
"Gemalto IDPrime",
|
|
|
|
"idprime",
|
|
|
|
&idprime_ops,
|
|
|
|
NULL, 0, NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
/* This ATR says, there is no EF.DIR nor EF.ATR so ISO discovery mechanisms
|
|
|
|
* are not useful here */
|
|
|
|
static const struct sc_atr_table idprime_atrs[] = {
|
|
|
|
{ "3b:7f:96:00:00:80:31:80:65:b0:84:41:3d:f6:12:0f:fe:82:90:00",
|
|
|
|
"ff:ff:00:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:ff:ff:ff:ff:ff:ff",
|
|
|
|
"Gemalto IDPrime MD 8840, 3840, 3810, 840 and 830 Cards",
|
|
|
|
SC_CARD_TYPE_IDPRIME_GENERIC, 0, NULL },
|
2020-02-03 15:40:51 +00:00
|
|
|
{ NULL, NULL, NULL, 0, 0, NULL }
|
2019-08-12 13:26:18 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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];
|
2019-09-30 13:09:36 +00:00
|
|
|
unsigned short length;
|
2019-08-12 13:26:18 +00:00
|
|
|
} 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 */
|
2019-09-30 13:01:55 +00:00
|
|
|
int tinfo_present; /* Token Info Label object is present*/
|
|
|
|
u8 tinfo_df[2]; /* DF of object with Token Info Label */
|
2019-08-12 13:26:18 +00:00
|
|
|
} 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);
|
2020-01-07 10:04:09 +00:00
|
|
|
if (r == SC_SUCCESS) {
|
|
|
|
r = file->size;
|
2019-08-12 13:26:18 +00:00
|
|
|
}
|
|
|
|
sc_file_free(file);
|
2020-01-07 10:04:09 +00:00
|
|
|
/* Ignore too large files */
|
2020-04-06 10:28:13 +00:00
|
|
|
if (r <= 0 || r > MAX_FILE_SIZE) {
|
2020-01-07 10:04:09 +00:00
|
|
|
r = SC_ERROR_INVALID_DATA;
|
|
|
|
}
|
2019-08-12 13:26:18 +00:00
|
|
|
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];
|
2019-09-30 13:09:36 +00:00
|
|
|
/* 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)) {
|
2019-08-12 13:26:18 +00:00
|
|
|
new_object.fd++;
|
2021-01-14 23:51:47 +00:00
|
|
|
if (card->type == SC_CARD_TYPE_IDPRIME_V1) {
|
|
|
|
/* The key reference is one bigger than the value found here for some reason */
|
|
|
|
new_object.key_reference = start[8] + 1;
|
|
|
|
} else {
|
2020-10-02 07:37:13 +00:00
|
|
|
int key_id = 0;
|
|
|
|
if (start[8] >= '0' && start[8] <= '9') {
|
|
|
|
key_id = start[8] - '0';
|
|
|
|
}
|
2021-05-06 17:28:49 +00:00
|
|
|
switch (card->type) {
|
|
|
|
case SC_CARD_TYPE_IDPRIME_V2:
|
2021-01-15 09:52:39 +00:00
|
|
|
new_object.key_reference = 0x11 + key_id;
|
2021-05-06 17:28:49 +00:00
|
|
|
break;
|
|
|
|
case SC_CARD_TYPE_IDPRIME_V3:
|
|
|
|
new_object.key_reference = 0xF7 + key_id;
|
|
|
|
break;
|
|
|
|
case SC_CARD_TYPE_IDPRIME_V4:
|
|
|
|
new_object.key_reference = 0x56 + key_id;
|
|
|
|
break;
|
2021-01-15 09:52:39 +00:00
|
|
|
}
|
2019-09-30 16:06:45 +00:00
|
|
|
}
|
|
|
|
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Found certificate with fd=%d, key_ref=%d",
|
|
|
|
new_object.fd, new_object.key_reference);
|
2019-08-12 13:26:18 +00:00
|
|
|
idprime_add_object_to_list(&priv->pki_list, &new_object);
|
2019-09-30 13:01:55 +00:00
|
|
|
|
|
|
|
/* 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");
|
2019-08-12 13:26:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
r = SC_SUCCESS;
|
|
|
|
done:
|
|
|
|
free(buf);
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
}
|
|
|
|
|
2019-09-30 16:06:45 +00:00
|
|
|
/* CPLC has 42 bytes, but we get it with 3B header */
|
|
|
|
#define CPLC_LENGTH 45
|
2019-08-12 13:26:18 +00:00
|
|
|
static int idprime_init(sc_card_t *card)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
unsigned long flags;
|
|
|
|
idprime_private_data_t *priv = NULL;
|
2019-09-30 16:06:45 +00:00
|
|
|
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;
|
2021-01-14 16:20:04 +00:00
|
|
|
case 0x03:
|
|
|
|
card->type = SC_CARD_TYPE_IDPRIME_V3;
|
|
|
|
sc_log(card->ctx, "Detected IDPrime applet version 3");
|
|
|
|
break;
|
2021-05-06 14:53:06 +00:00
|
|
|
case 0x04:
|
|
|
|
card->type = SC_CARD_TYPE_IDPRIME_V4;
|
|
|
|
sc_log(card->ctx, "Detected IDPrime applet version 4");
|
|
|
|
break;
|
2019-09-30 16:06:45 +00:00
|
|
|
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);
|
|
|
|
}
|
2019-08-12 13:26:18 +00:00
|
|
|
|
2019-09-30 16:06:45 +00:00
|
|
|
/* Now, select and process the index file */
|
2019-08-12 13:26:18 +00:00
|
|
|
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;
|
|
|
|
}
|
2019-09-30 16:06:45 +00:00
|
|
|
|
2019-08-12 13:26:18 +00:00
|
|
|
r = idprime_process_index(card, priv, r);
|
|
|
|
if (r != SC_SUCCESS) {
|
|
|
|
idprime_free_private_data(priv);
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
}
|
2019-09-30 16:06:45 +00:00
|
|
|
|
2019-08-12 13:26:18 +00:00
|
|
|
card->drv_data = priv;
|
|
|
|
|
2020-01-07 11:48:21 +00:00
|
|
|
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;
|
2021-05-06 17:28:36 +00:00
|
|
|
case SC_CARD_TYPE_IDPRIME_V3:
|
|
|
|
card->name = "Gemalto IDPrime (OSv3)";
|
|
|
|
break;
|
|
|
|
case SC_CARD_TYPE_IDPRIME_V4:
|
|
|
|
card->name = "Gemalto IDPrime (OSv4)";
|
|
|
|
break;
|
2020-01-07 11:48:21 +00:00
|
|
|
case SC_CARD_TYPE_IDPRIME_GENERIC:
|
|
|
|
default:
|
|
|
|
card->name = "Gemalto IDPrime (generic)";
|
|
|
|
break;
|
|
|
|
}
|
2019-08-12 13:26:18 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-09-30 13:01:55 +00:00
|
|
|
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];
|
2020-12-02 12:15:11 +00:00
|
|
|
char *name;
|
2019-09-30 13:01:55 +00:00
|
|
|
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);
|
|
|
|
|
2020-12-02 12:15:11 +00:00
|
|
|
name = malloc(buf[1]);
|
|
|
|
if (name == NULL) {
|
2019-09-30 13:01:55 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
|
|
|
|
}
|
|
|
|
|
2020-12-02 12:15:11 +00:00
|
|
|
r = iso_ops->read_binary(card, 2, (unsigned char *)name, buf[1], 0);
|
2019-09-30 13:01:55 +00:00
|
|
|
if (r < 1) {
|
2020-12-02 12:15:11 +00:00
|
|
|
free(name);
|
2019-09-30 13:01:55 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
}
|
|
|
|
|
2020-12-02 12:15:11 +00:00
|
|
|
if (name[r-1] != '\0') {
|
|
|
|
name[r-1] = '\0';
|
2019-09-30 13:01:55 +00:00
|
|
|
}
|
2020-12-02 12:15:11 +00:00
|
|
|
*tname = name;
|
|
|
|
|
2019-09-30 13:01:55 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2019-08-12 13:26:18 +00:00
|
|
|
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);
|
2019-09-30 13:01:55 +00:00
|
|
|
case SC_CARDCTL_IDPRIME_GET_TOKEN_NAME:
|
|
|
|
return idprime_get_token_name(card, (char **) ptr);
|
2019-08-12 13:26:18 +00:00
|
|
|
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);
|
2020-01-31 15:27:06 +00:00
|
|
|
if (r == SC_SUCCESS && file_out != NULL) {
|
2019-08-12 13:26:18 +00:00
|
|
|
/* 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;
|
2020-09-24 17:08:57 +00:00
|
|
|
int r = 0;
|
2019-08-12 13:26:18 +00:00
|
|
|
int size;
|
|
|
|
|
|
|
|
sc_log(card->ctx, "called; %"SC_FORMAT_LEN_SIZE_T"u bytes at offset %d",
|
|
|
|
count, offset);
|
|
|
|
|
|
|
|
if (!priv->cached && offset == 0) {
|
2020-09-24 17:08:57 +00:00
|
|
|
/* Read what was reported by FCI from select command */
|
|
|
|
int left = priv->file_size;
|
|
|
|
size_t read = 0;
|
|
|
|
|
2019-08-12 13:26:18 +00:00
|
|
|
// this function is called to read and uncompress the certificate
|
|
|
|
u8 buffer[SC_MAX_EXT_APDU_BUFFER_SIZE];
|
2020-09-30 21:44:00 +00:00
|
|
|
if (sizeof(buffer) < count || sizeof(buffer) < priv->file_size) {
|
2019-08-12 13:26:18 +00:00
|
|
|
LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
|
|
|
|
}
|
2020-09-24 17:08:57 +00:00
|
|
|
while (left > 0) {
|
|
|
|
r = iso_ops->read_binary(card, read, buffer + read, priv->file_size - read, flags);
|
|
|
|
if (r <= 0) {
|
|
|
|
LOG_FUNC_RETURN(card->ctx, r);
|
|
|
|
}
|
|
|
|
left -= r;
|
|
|
|
read += r;
|
2019-08-12 13:26:18 +00:00
|
|
|
}
|
2020-09-24 17:08:57 +00:00
|
|
|
if (read < 4 || read != priv->file_size) {
|
2019-08-12 13:26:18 +00:00
|
|
|
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) {
|
2021-01-18 10:37:59 +00:00
|
|
|
if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA1) {
|
2019-08-12 13:26:18 +00:00
|
|
|
new_env.algorithm_ref = 0x1D;
|
2021-01-18 10:37:59 +00:00
|
|
|
} else if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA256) {
|
2019-08-12 13:26:18 +00:00
|
|
|
new_env.algorithm_ref = 0x4D;
|
2021-01-18 10:37:59 +00:00
|
|
|
} else if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA384) {
|
2019-08-12 13:26:18 +00:00
|
|
|
new_env.algorithm_ref = 0x5D;
|
2021-01-18 10:37:59 +00:00
|
|
|
} else if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA512) {
|
2019-08-12 13:26:18 +00:00
|
|
|
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) {
|
2021-01-18 10:37:59 +00:00
|
|
|
if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA256) {
|
2019-08-12 13:26:18 +00:00
|
|
|
new_env.algorithm_ref = 0x45;
|
2021-01-18 10:37:59 +00:00
|
|
|
} else if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA384) {
|
2019-08-12 13:26:18 +00:00
|
|
|
new_env.algorithm_ref = 0x55;
|
2021-01-18 10:37:59 +00:00
|
|
|
} else if (env->algorithm_flags & SC_ALGORITHM_MGF1_SHA512) {
|
2019-08-12 13:26:18 +00:00
|
|
|
new_env.algorithm_ref = 0x65;
|
|
|
|
}
|
|
|
|
} else { /* RSA-PKCS */
|
2021-05-17 14:20:49 +00:00
|
|
|
if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA256) {
|
2019-08-12 13:26:18 +00:00
|
|
|
new_env.algorithm_ref = 0x42;
|
2021-05-17 14:20:49 +00:00
|
|
|
} else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA384) {
|
2019-08-12 13:26:18 +00:00
|
|
|
new_env.algorithm_ref = 0x52;
|
2021-05-17 14:20:49 +00:00
|
|
|
} else if (env->algorithm_flags & SC_ALGORITHM_RSA_HASH_SHA512) {
|
2019-08-12 13:26:18 +00:00
|
|
|
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();
|
|
|
|
}
|