Implement new Gemalto IDPrime driver

The card is largely ISO 7816 compliant, but does not provide any
simple way of listing the content which is supported by current
PKCS#15 implementation therefore the PKCS#15 emulator had to be
used.

The certificates are compressed in a similar way as in DNIE
cards which complicates reading from the card and which I think
could be moved to the shared ISO (or some other file since I saw
that code already many times).

The card supports wide range of algorithms including
RSA-PSS and RSA-OAEP padding schemes in-card. On the other hand,
it does not allow raw RSA and SHA1 hashes on card anymore.

The card is manufactured by Gemalto so it has strict ATR which
can be used for detection.
This commit is contained in:
Jakub Jelen 2019-08-12 15:26:18 +02:00
parent 3a3a465e6b
commit f61d9b3b53
9 changed files with 992 additions and 13 deletions

View File

@ -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 \

View File

@ -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

View File

@ -0,0 +1,687 @@
/*
* card-idprime.c: Support for Gemalto IDPrime smart cards
*
* Copyright (c) 2019 Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "internal.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#ifdef ENABLE_ZLIB
#include "compression.h"
#endif
#include "cardctl.h"
#include "pkcs15.h"
static const struct sc_card_operations *iso_ops = NULL;
static struct sc_card_operations idprime_ops;
static struct sc_card_driver idprime_drv = {
"Gemalto IDPrime",
"idprime",
&idprime_ops,
NULL, 0, NULL
};
/* This ATR says, there is no EF.DIR nor EF.ATR so ISO discovery mechanisms
* are not useful here */
static const struct sc_atr_table idprime_atrs[] = {
{ "3b:7f:96:00:00:80:31:80:65:b0:84:41:3d:f6:12:0f:fe:82:90:00",
"ff:ff:00:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:ff:ff:ff:ff:ff:ff",
"Gemalto IDPrime MD 8840, 3840, 3810, 840 and 830 Cards",
SC_CARD_TYPE_IDPRIME_GENERIC, 0, NULL },
};
static const sc_path_t idprime_path = {
"", 0,
0, 0, SC_PATH_TYPE_DF_NAME,
{ "\xA0\x00\x00\x00\x18\x80\x00\x00\x00\x06\x62", 11 }
};
/* data structures to store meta data about IDPrime objects */
typedef struct idprime_object {
int fd;
unsigned char key_reference;
u8 df[2];
} 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();
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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. */

View File

@ -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 <jjelen@redhat.com>
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <stdlib.h>
#include "internal.h"
#include "cardctl.h"
#include "pkcs15.h"
#define CERT_LABEL_TEMPLATE "Certificate %d"
#define PUBKEY_LABEL_TEMPLATE "Public key %d"
#define PRIVKEY_LABEL_TEMPLATE "Private key %d"
static int idprime_detect_card(sc_pkcs15_card_t *p15card)
{
sc_card_t *card = p15card->card;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
if (card->type < SC_CARD_TYPE_IDPRIME_BASE
|| card->type >= SC_CARD_TYPE_IDPRIME_BASE+1000)
return SC_ERROR_INVALID_CARD;
return SC_SUCCESS;
}
static int sc_pkcs15emu_idprime_init(sc_pkcs15_card_t *p15card)
{
int r, i;
sc_card_t *card = p15card->card;
sc_serial_number_t serial;
char buf[SC_MAX_SERIALNR * 2 + 1];
int count;
char *token_name = NULL;
struct sc_pkcs15_auth_info pin_info;
struct sc_pkcs15_object pin_obj;
const char pin_label[] = "PIN";
const char *pin_id = "11";
/* oid for key usage */
static const struct sc_object_id usage_type = {{ 2, 5, 29, 15, -1 }};
unsigned int usage;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
/* could read this off card if needed */
p15card->tokeninfo->label = strdup("IDPrime");
p15card->tokeninfo->manufacturer_id = strdup("Gemalto");
/*
* get serial number
*/
memset(&serial, 0, sizeof(serial));
r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial);
if (r < 0) {
sc_log(card->ctx, "sc_card_ctl rc=%d", r);
p15card->tokeninfo->serial_number = strdup("00000000");
} else {
sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0);
p15card->tokeninfo->serial_number = strdup(buf);
}
/* set pin */
sc_log(card->ctx, "IDPrime adding pin...");
memset(&pin_info, 0, sizeof(pin_info));
memset(&pin_obj, 0, sizeof(pin_obj));
pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;
sc_pkcs15_format_id(pin_id, &pin_info.auth_id);
pin_info.attrs.pin.reference = 0x11;
pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_INITIALIZED;
pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC;
pin_info.attrs.pin.min_length = 4;
pin_info.attrs.pin.stored_length = 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);
}

View File

@ -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 }
};

View File

@ -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;