added support for QES with German ID card

libnpa: requires an ID card that is initialized for QES. Today, this
means you need to register at sign-me.

virtualsmartcard: We don't actually create a real signature, because
PyCrypto can't handle ECDSA, let alone brainpoolP256r1. We only return a
64 byte buffer.
This commit is contained in:
Frank Morgner
2015-01-18 00:55:29 +01:00
parent ff71e301db
commit d89385c3e1
23 changed files with 555 additions and 424 deletions

View File

@@ -27,11 +27,6 @@ libcardnpa_la_LIBADD = $(OPENSC_LIBS) libnpa.la
libcardnpa_la_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS)
libcardnpa_la_LDFLAGS = -no-undefined
libpkcs15npa_la_SOURCES = pkcs15-npa.c
libpkcs15npa_la_LIBADD = $(OPENSC_LIBS) libcardnpa.la libnpa.la
libpkcs15npa_la_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS)
libpkcs15npa_la_LDFLAGS = -no-undefined
npa_tool_SOURCES = npa-tool.c $(BUILT_SOURCES)
npa_tool_LDADD = libnpa.la $(OPENSC_LIBS) $(OPENPACE_LIBS) $(OPENSSL_LIBS)
npa_tool_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS)
@@ -61,7 +56,7 @@ $(abs_builddir)/npa-tool.1: npa-tool.ggo
bin_PROGRAMS = npa-tool
noinst_PROGRAMS = example
lib_LTLIBRARIES = libnpa.la libcardnpa.la libpkcs15npa.la
lib_LTLIBRARIES = libnpa.la libcardnpa.la
noinst_HEADERS = \
sslutil.h \

View File

@@ -24,12 +24,54 @@
#include "card-npa.h"
#include <npa/boxing.h>
#include <npa/npa.h>
#include <npa/scutil.h>
#include <string.h>
struct npa_drv_data {
const char *can;
unsigned char *st_dv_certificate;
size_t st_dv_certificate_len;
unsigned char *st_certificate;
size_t st_certificate_len;
unsigned char *st_key;
size_t st_key_len;
unsigned char *ef_cardaccess;
size_t ef_cardaccess_length;
unsigned char *ef_cardsecurity;
size_t ef_cardsecurity_length;
};
static struct npa_drv_data *npa_drv_data_create(void)
{
struct npa_drv_data *drv_data = calloc(1, sizeof *drv_data);
return drv_data;
}
static void npa_drv_data_free(struct npa_drv_data *drv_data)
{
if (drv_data) {
free(drv_data->ef_cardaccess);
free(drv_data->ef_cardsecurity);
free(drv_data->st_certificate);
free(drv_data->st_dv_certificate);
free(drv_data->st_key);
free(drv_data);
}
}
static struct sc_atr_table npa_atrs[] = {
{"3B:8A:80:01:80:31:F8:73:F7:41:E0:82:90:00:75", NULL, "German ID card (neuer Personalausweis, nPA)", SC_CARD_TYPE_NPA, 0, NULL},
{"3B:84:80:01:00:00:90:00:95", NULL, "German ID card (Test neuer Personalausweis)", SC_CARD_TYPE_NPA_TEST, 0, NULL},
{"3B:88:80:01:00:E1:F3:5E:13:77:83:00:00", "FF:FF:FF:FF:00:FF:FF:FF:FF:FF:FF:FF:00", "German ID card (Test Online-Ausweisfunktion)", SC_CARD_TYPE_NPA_ONLINE, 0, NULL},
{"3B:8A:80:01:80:31:F8:73:F7:41:E0:82:90:00:75",
"FF:FF:FF:FF:FF:FF:00:FF:00:00:FF:FF:FF:FF:00",
"German ID card (neuer Personalausweis, nPA)", SC_CARD_TYPE_NPA, 0, NULL},
{"3B:88:80:01:00:00:00:00:00:00:00:00:09", NULL,
"German ID card (neuer Personalausweis, nPA)", SC_CARD_TYPE_NPA, 0, NULL},
{"3B:87:80:01:80:31:B8:73:84:01:E0:19", NULL,
"German ID card (neuer Personalausweis, nPA)", SC_CARD_TYPE_NPA, 0, NULL},
{"3B:84:80:01:00:00:90:00:95", NULL,
"German ID card (Test neuer Personalausweis)", SC_CARD_TYPE_NPA_TEST, 0, NULL},
{"3B:88:80:01:00:E1:F3:5E:13:77:83:00:00",
"FF:FF:FF:FF:00:FF:FF:FF:FF:FF:FF:FF:00",
"German ID card (Test Online-Ausweisfunktion)", SC_CARD_TYPE_NPA_ONLINE, 0, NULL},
{NULL, NULL, NULL, 0, 0, NULL}
};
@@ -41,6 +83,64 @@ static struct sc_card_driver npa_drv = {
NULL, 0, NULL
};
static int npa_load_options(sc_context_t *ctx, struct npa_drv_data *drv_data)
{
int r;
size_t i, j;
scconf_block **found_blocks, *block;
const char *file;
if (!ctx || !drv_data) {
r = SC_ERROR_INTERNAL;
goto err;
}
for (i = 0; ctx->conf_blocks && ctx->conf_blocks[i] != NULL; i++) {
found_blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i],
"card_driver", "npa");
if (!found_blocks)
continue;
for (j = 0, block = found_blocks[j]; block; j++, block = found_blocks[j]) {
if (!drv_data->can)
drv_data->can = scconf_get_str(block, "can", NULL);
if (!drv_data->st_dv_certificate
|| !drv_data->st_dv_certificate_len) {
file = scconf_get_str(block, "st_dv_certificate", NULL);
if (!fread_to_eof(file,
(unsigned char **) &drv_data->st_dv_certificate,
&drv_data->st_dv_certificate_len))
sc_log(ctx, "Waring: Could not read %s.\n", file);
}
if (!drv_data->st_certificate
|| !drv_data->st_certificate_len) {
file = scconf_get_str(block, "st_certificate", NULL);
if (!fread_to_eof(file,
(unsigned char **) &drv_data->st_certificate,
&drv_data->st_certificate_len))
sc_log(ctx, "Waring: Could not read %s.\n", file);
}
if (!drv_data->st_key
|| !drv_data->st_key_len) {
file = scconf_get_str(block, "st_key", NULL);
if (!fread_to_eof(file,
(unsigned char **) &drv_data->st_key,
&drv_data->st_key_len))
sc_log(ctx, "Waring: Could not read %s.\n", file);
}
}
free(found_blocks);
}
r = SC_SUCCESS;
err:
return r;
}
static int npa_match_card(sc_card_t * card)
{
if (_sc_match_atr(card, npa_atrs, &card->type) < 0)
@@ -48,32 +148,267 @@ static int npa_match_card(sc_card_t * card)
return 1;
}
static int npa_init(sc_card_t * card)
static void npa_get_cached_pace_params(sc_card_t *card,
struct establish_pace_channel_input *pace_input,
struct establish_pace_channel_output *pace_output)
{
if (card) {
#if 0
/* we wait for https://github.com/OpenSC/OpenSC/pull/260 to be
* integrated before switching extended length on, here */
card->max_recv_size = 0xFFFF+1;
card->max_send_size = 0xFFFF;
#endif
card->caps |= SC_CARD_CAP_APDU_EXT | SC_CARD_CAP_RNG;
memset(&card->sm_ctx, 0, sizeof card->sm_ctx);
#ifdef DISABLE_GLOBAL_BOXING_INITIALIZATION
sc_detect_boxing_cmds(card->reader);
#endif
struct npa_drv_data *drv_data;
if (card->drv_data) {
drv_data = card->drv_data;
if (pace_output) {
pace_output->ef_cardaccess = drv_data->ef_cardaccess;
pace_output->ef_cardaccess_length = drv_data->ef_cardaccess_length;
}
if (pace_input && pace_input->pin_id == PACE_PIN_ID_CAN) {
pace_input->pin = (const unsigned char *) drv_data->can;
pace_input->pin_length = drv_data->can ? strlen(drv_data->can) : 0;
}
}
}
static void npa_get_cached_ta_params(sc_card_t *card,
const unsigned char *certs[2], size_t certs_lens[2],
const unsigned char **st_key, size_t *st_key_len)
{
struct npa_drv_data *drv_data;
size_t i;
if (card->drv_data) {
drv_data = card->drv_data;
if (certs && certs_lens) {
i = 0;
if (drv_data->st_dv_certificate) {
certs[i] = drv_data->st_dv_certificate;
certs_lens[i] = drv_data->st_dv_certificate_len;
i++;
}
if (drv_data->st_certificate) {
certs[i] = drv_data->st_certificate;
certs_lens[i] = drv_data->st_certificate_len;
}
}
if (st_key && st_key_len) {
*st_key = drv_data->st_key;
*st_key_len = drv_data->st_key_len;
}
}
}
static void npa_get_cached_ca_params(sc_card_t *card,
unsigned char **ef_cardsecurity, size_t *ef_cardsecurity_length)
{
struct npa_drv_data *drv_data;
if (card->drv_data) {
drv_data = card->drv_data;
if (ef_cardsecurity && ef_cardsecurity_length) {
*ef_cardsecurity = drv_data->ef_cardsecurity;
*ef_cardsecurity_length = drv_data->ef_cardsecurity_length;
}
}
}
static void npa_cache_or_free(sc_card_t *card,
unsigned char **ef_cardaccess, size_t *ef_cardaccess_length,
unsigned char **ef_cardsecurity, size_t *ef_cardsecurity_length)
{
struct npa_drv_data *drv_data;
if (card->drv_data) {
drv_data = card->drv_data;
if (ef_cardaccess && ef_cardaccess_length
&& *ef_cardaccess && *ef_cardaccess_length) {
drv_data->ef_cardaccess = *ef_cardaccess;
drv_data->ef_cardaccess_length = *ef_cardaccess_length;
*ef_cardaccess = NULL;
*ef_cardaccess_length = 0;
}
if (ef_cardsecurity && ef_cardsecurity_length
&& *ef_cardsecurity && *ef_cardsecurity_length) {
drv_data->ef_cardsecurity = *ef_cardsecurity;
drv_data->ef_cardsecurity_length = *ef_cardsecurity_length;
*ef_cardsecurity = NULL;
*ef_cardsecurity_length = 0;
}
} else {
if (ef_cardaccess && ef_cardaccess_length) {
free(*ef_cardaccess);
*ef_cardaccess = NULL;
*ef_cardaccess_length = 0;
}
if (ef_cardsecurity && ef_cardsecurity_length) {
free(*ef_cardsecurity);
*ef_cardsecurity = NULL;
*ef_cardsecurity_length = 0;
}
}
}
static int npa_unlock_esign(sc_card_t *card)
{
int r = SC_ERROR_INTERNAL;
struct establish_pace_channel_input pace_input;
struct establish_pace_channel_output pace_output;
const unsigned char *certs[] = { NULL, NULL };
size_t certs_lens[] = { 0, 0};
const unsigned char *st_key = NULL;
size_t st_key_len = 0;
unsigned char *ef_cardsecurity = NULL;
size_t ef_cardsecurity_len = 0;
memset(&pace_input, 0, sizeof pace_input);
memset(&pace_output, 0, sizeof pace_output);
if (!card) {
r = SC_ERROR_INVALID_CARD;
goto err;
}
return SC_SUCCESS;
sc_log(card->ctx, "Will verify CAN first for unlocking eSign application.\n");
pace_input.chat = esign_chat;
pace_input.chat_length = sizeof esign_chat;
pace_input.pin_id = PACE_PIN_ID_CAN;
npa_get_cached_pace_params(card, &pace_input, &pace_output);
npa_get_cached_ta_params(card, certs, certs_lens, &st_key, &st_key_len);
npa_get_cached_ca_params(card, &ef_cardsecurity, &ef_cardsecurity_len);
if (!(card->reader && (card->reader->capabilities & SC_READER_CAP_PACE_ESIGN))
&& (!st_key || !st_key_len)) {
sc_log(card->ctx, "QES requires a comfort reader (CAT-K) or a ST certificate.\n");
r = SC_ERROR_NOT_SUPPORTED;
}
/* FIXME set flags with opensc.conf */
npa_default_flags |= NPA_FLAG_DISABLE_CHECK_ALL;
npa_default_flags |= NPA_FLAG_DISABLE_CHECK_TA;
npa_default_flags |= NPA_FLAG_DISABLE_CHECK_CA;
/* FIXME show an alert to the user if can == NULL */
r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02);
if (SC_SUCCESS != r) {
sc_log(card->ctx, "Error verifying CAN.\n");
goto err;
}
if (card->reader->capabilities & SC_READER_CAP_PACE_ESIGN) {
sc_log(card->ctx, "Proved Access rights to eSign application with comfort reader (CAT-K).\n");
} else {
r = perform_terminal_authentication(card, certs, certs_lens, st_key,
st_key_len, NULL, 0);
if (r != SC_SUCCESS) {
sc_log(card->ctx, "Error authenticating as signature terminal.\n");
goto err;
}
r = perform_chip_authentication(card, &ef_cardsecurity, &ef_cardsecurity_len);
if ( SC_SUCCESS != r) {
sc_log(card->ctx, "Error verifying the chips authenticy.\n");
}
sc_log(card->ctx, "Proved Access rights to eSign application with configured key as ST.\n");
}
err:
npa_cache_or_free(card, &pace_output.ef_cardaccess,
&pace_output.ef_cardaccess_length,
&ef_cardsecurity, &ef_cardsecurity_len);
free(pace_output.recent_car);
free(pace_output.previous_car);
free(pace_output.id_icc);
free(pace_output.id_pcd);
return r;
}
static int npa_init(sc_card_t * card)
{
int flags = SC_ALGORITHM_ECDSA_RAW;
int ext_flags = 0;
int r;
if (!card) {
r = SC_ERROR_INVALID_CARD;
goto err;
}
card->caps |= SC_CARD_CAP_APDU_EXT | SC_CARD_CAP_RNG;
card->max_recv_size = 0xFFFF+1;
card->max_send_size = 0xFFFF;
memset(&card->sm_ctx, 0, sizeof card->sm_ctx);
r = _sc_card_add_ec_alg(card, 192, flags, ext_flags);
if (r != SC_SUCCESS)
goto err;
r = _sc_card_add_ec_alg(card, 224, flags, ext_flags);
if (r != SC_SUCCESS)
goto err;
r = _sc_card_add_ec_alg(card, 256, flags, ext_flags);
if (r != SC_SUCCESS)
goto err;
/* nPA does not encode the proprietary fieldSize in PrivateECKeyAttributes,
* which leaves it at 0 for OpenSC, so we need to add 0x00 as supported
* field_length */
r = _sc_card_add_ec_alg(card, 0, flags, ext_flags);
if (r != SC_SUCCESS)
goto err;
#ifdef DISABLE_GLOBAL_BOXING_INITIALIZATION
sc_detect_boxing_cmds(card->reader);
#endif
EAC_init();
card->drv_data = npa_drv_data_create();
r = npa_load_options(card->ctx, card->drv_data);
if (r != SC_SUCCESS)
goto err;
/* unlock the eSign application for reading the certificates
* by the PKCS#15 layer (i.e. sc_pkcs15_bind_internal) */
r = npa_unlock_esign(card);
err:
return r;
}
static int npa_finish(sc_card_t * card)
{
sm_stop(card);
npa_drv_data_free(card->drv_data);
card->drv_data = NULL;
EAC_cleanup();
return SC_SUCCESS;
}
static int npa_set_security_env(struct sc_card *card,
const struct sc_security_env *env, int se_num)
{
int r;
struct sc_card_driver *iso_drv;
struct sc_security_env fixed_env;
iso_drv = sc_get_iso7816_driver();
if (!env || !iso_drv || !iso_drv->ops || !iso_drv->ops->set_security_env) {
r = SC_ERROR_INTERNAL;
} else {
memcpy(&fixed_env, env, sizeof fixed_env);
if (env->operation == SC_SEC_OPERATION_SIGN) {
/* The pkcs#15 layer assumes that the field_size of the private key
* object is correctly initialized and wants to include it as
* algorithm reference. We disable it here */
fixed_env.flags &= ~SC_SEC_ENV_ALG_REF_PRESENT;
}
r = iso_drv->ops->set_security_env(card, &fixed_env, se_num);
}
return r;
}
static int npa_pin_cmd_get_info(struct sc_card *card,
struct sc_pin_cmd_data *data, int *tries_left)
{
@@ -134,6 +469,7 @@ static int npa_pace_verify(struct sc_card *card,
pace_input.pin = pin->data;
pace_input.pin_length = pin->len;
}
npa_get_cached_pace_params(card, &pace_input, &pace_output);
r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02);
@@ -199,7 +535,8 @@ static int npa_pace_verify(struct sc_card *card,
}
}
free(pace_output.ef_cardaccess);
npa_cache_or_free(card, &pace_output.ef_cardaccess,
&pace_output.ef_cardaccess_length, NULL, NULL);
free(pace_output.recent_car);
free(pace_output.previous_car);
free(pace_output.id_icc);
@@ -281,27 +618,17 @@ static int npa_pin_cmd(struct sc_card *card,
goto err;
break;
case NPA_PIN_ID_ESIGN_PIN:
if (card->reader->capabilities & SC_READER_CAP_PACE_ESIGN) {
sc_log(card->ctx, "Found a comfort reader (CAT-K).\n");
sc_log(card->ctx, "Will verify CAN first.\n");
r = npa_pace_verify(card, PACE_PIN_ID_CAN, NULL,
esign_chat, sizeof esign_chat, tries_left);
if (r != SC_SUCCESS)
goto err;
} else {
sc_log(card->ctx, "No comfort reader (CAT-K) found.\n");
sc_log(card->ctx, "I hope you have already performed EAC as ST...\n");
}
default:
/* assuming QES PIN */
/* We assume that the eSign application has already been
* unlocked, see npa_init().
*
* Now, verify the QES PIN. */
r = npa_standard_pin_cmd(card, data, tries_left);
if (r != SC_SUCCESS)
goto err;
break;
default:
r = SC_ERROR_OBJECT_NOT_FOUND;
goto err;
break;
}
if (data->cmd == SC_PIN_CMD_CHANGE) {
@@ -337,6 +664,7 @@ struct sc_card_driver *npa_get_driver(void)
npa_ops.match_card = npa_match_card;
npa_ops.init = npa_init;
npa_ops.finish = npa_finish;
npa_ops.set_security_env = npa_set_security_env;
npa_ops.pin_cmd = npa_pin_cmd;
npa_ops.logout = npa_logout;

View File

@@ -32,8 +32,6 @@ enum {
SC_CARD_TYPE_NPA_ONLINE,
};
#define NPA_PIN_ID_ESIGN_PIN 0x83
const unsigned char esign_chat[] = {
0x7F, 0x4C, 0x0E,
0x06, 0x09, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x03, 0x01, 0x02, 0x03,

View File

@@ -53,44 +53,6 @@ static ssize_t getline(char **lineptr, size_t *n, FILE *stream)
}
#endif
int fread_to_eof(const char *file, unsigned char **buf, size_t *buflen)
{
FILE *input = NULL;
int r = 0;
unsigned char *p;
if (!buflen || !buf)
goto err;
#define MAX_READ_LEN 0xfff
p = realloc(*buf, MAX_READ_LEN);
if (!p)
goto err;
*buf = p;
input = fopen(file, "rb");
if (!input) {
fprintf(stderr, "Could not open %s.\n", file);
goto err;
}
*buflen = 0;
while (feof(input) == 0 && *buflen < MAX_READ_LEN) {
*buflen += fread(*buf+*buflen, 1, MAX_READ_LEN-*buflen, input);
if (ferror(input)) {
fprintf(stderr, "Could not read %s.\n", file);
goto err;
}
}
r = 1;
err:
if (input)
fclose(input);
return r;
}
static void read_dg(sc_card_t *card, unsigned char sfid, const char *dg_str,
unsigned char **dg, size_t *dg_len)
{
@@ -299,6 +261,8 @@ main (int argc, char **argv)
unsigned char *dg = NULL;
size_t dg_len = 0;
ASN1_AUXILIARY_DATA *templates = NULL;
unsigned char *ef_cardsecurity = NULL;
size_t ef_cardsecurity_len = 0;
struct gengetopt_args_info cmdline;
@@ -695,7 +659,7 @@ nopace:
goto err;
printf("Performed Terminal Authentication.\n");
r = perform_chip_authentication(card);
r = perform_chip_authentication(card, &ef_cardsecurity, &ef_cardsecurity_len);
if (r < 0)
goto err;
printf("Performed Chip Authentication.\n");
@@ -803,6 +767,10 @@ err:
free(pace_output.previous_car);
free(pace_output.id_icc);
free(pace_output.id_pcd);
if (ef_cardsecurity) {
OPENSSL_cleanse(ef_cardsecurity, ef_cardsecurity_len);
free(ef_cardsecurity);
}
if (input)
fclose(input);
if (certs) {

View File

@@ -1842,15 +1842,15 @@ static int get_ef_card_security(sc_card_t *card,
return read_binary_rec(card, SFID_EF_CARDSECURITY, ef_security, length_ef_security);
}
int perform_chip_authentication(sc_card_t *card)
int perform_chip_authentication(sc_card_t *card,
unsigned char **ef_cardsecurity, size_t *ef_cardsecurity_len)
{
int r;
BUF_MEM *picc_pubkey = NULL, *nonce = NULL, *token = NULL,
*eph_pub_key = NULL;
unsigned char *ef_cardsecurity = NULL;
size_t ef_cardsecurity_len;
if (!card || !card->sm_ctx.info.cmd_data) {
if (!card || !card->sm_ctx.info.cmd_data
|| !ef_cardsecurity || !ef_cardsecurity_len) {
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
@@ -1863,12 +1863,14 @@ int perform_chip_authentication(sc_card_t *card)
/* Passive Authentication */
r = get_ef_card_security(card, &ef_cardsecurity, &ef_cardsecurity_len);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not get EF.CardSecurity.");
goto err;
if (!*ef_cardsecurity && !*ef_cardsecurity_len) {
r = get_ef_card_security(card, ef_cardsecurity, ef_cardsecurity_len);
if (r < 0 || !ef_cardsecurity || !ef_cardsecurity_len) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not get EF.CardSecurity.");
goto err;
}
}
picc_pubkey = CA_get_pubkey(eacsmctx->ctx, ef_cardsecurity, ef_cardsecurity_len);
picc_pubkey = CA_get_pubkey(eacsmctx->ctx, *ef_cardsecurity, *ef_cardsecurity_len);
if (!picc_pubkey) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not verify EF.CardSecurity.");
ssl_error(card->ctx);
@@ -1924,10 +1926,6 @@ int perform_chip_authentication(sc_card_t *card)
}
err:
if (ef_cardsecurity) {
OPENSSL_cleanse(ef_cardsecurity, ef_cardsecurity_len);
free(ef_cardsecurity);
}
BUF_MEM_clear_free(picc_pubkey);
BUF_MEM_clear_free(nonce);
BUF_MEM_clear_free(token);

View File

@@ -180,10 +180,13 @@ int perform_terminal_authentication(sc_card_t *card,
* Switches the SM context of \c card to the new established keys.
*
* @param[in] card
* @param[in,out] ef_cardsecurity
* @param[in,out] ef_cardsecurity_len
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int perform_chip_authentication(sc_card_t *card);
int perform_chip_authentication(sc_card_t *card,
unsigned char **ef_cardsecurity, size_t *ef_cardsecurity_len);
/**
* @brief Sends a reset retry counter APDU

View File

@@ -147,5 +147,7 @@ int sc_apdu_set_resp(sc_context_t *ctx, sc_apdu_t *apdu, const u8 *buf,
* be null terminated. */
int _sc_match_atr(struct sc_card *card, struct sc_atr_table *table, int *type_out);
int fread_to_eof(const char *file, unsigned char **buf, size_t *buflen);
#endif
/* @} */

View File

@@ -1,261 +0,0 @@
/*
* pkcs15-npa.c: PKCS#15 emulation for German ID card
*
* Copyright (C) 2014 Frank Morgner <morgner@informatik.hu-berlin.de>
*
* 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
*/
#include "libopensc/log.h"
#include "libopensc/opensc.h"
#include "libopensc/pace.h"
#include "libopensc/pkcs15.h"
#include "card-npa.h"
#include <npa/npa.h>
#include <stdlib.h>
#include <string.h>
static int npa_detect_card(sc_pkcs15_card_t *p15card)
{
int r = SC_ERROR_WRONG_CARD;
if (p15card && p15card->card
&& (p15card->card->type == SC_CARD_TYPE_NPA
|| p15card->card->type == SC_CARD_TYPE_NPA_TEST
|| p15card->card->type == SC_CARD_TYPE_NPA_ONLINE)) {
r = SC_SUCCESS;
}
return r;
}
static int npa_add_pin(sc_pkcs15_card_t *p15card,
const char *label, int max_tries,
unsigned int flags, size_t min_length,
size_t max_length, unsigned char reference,
unsigned char auth_id, const struct sc_path *path,
const char *pin, size_t pin_length)
{
struct sc_pkcs15_auth_info pin_info;
struct sc_pkcs15_object pin_obj;
memset(&pin_info, 0, sizeof pin_info);
memset(&pin_obj, 0, sizeof(pin_obj));
strncpy(pin_obj.label, label, sizeof pin_obj.label);
pin_obj.label[(sizeof pin_obj.label) - 1]= '\0';
if (auth_id) {
pin_obj.auth_id.len = sizeof auth_id;
memcpy(&pin_obj.auth_id.value, &auth_id, sizeof auth_id);
}
pin_info.auth_id.len = sizeof reference;
memcpy(&pin_info.auth_id.value, &reference, sizeof reference);
pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;
pin_info.auth_method = SC_AC_CHV;
pin_info.tries_left = -1;
pin_info.max_tries = max_tries;
if (path)
memcpy(&pin_info.path, path, sizeof *path);
pin_info.attrs.pin.flags = flags;
pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC;
pin_info.attrs.pin.min_length = min_length;
pin_info.attrs.pin.max_length = max_length;
pin_info.attrs.pin.reference = reference;
if (pin && pin_length) {
sc_pkcs15_pincache_add(p15card, &pin_obj, pin, pin_length);
}
return sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info);
}
static int npa_add_pins(sc_pkcs15_card_t *p15card, const char *can)
{
int r;
const sc_path_t *mf = sc_get_mf_path();
sc_path_t df_esign;
r = sc_path_set(&df_esign, SC_PATH_TYPE_PATH,
df_esign_path, sizeof df_esign_path, 0, 0);
if (r != SC_SUCCESS)
goto err;
r = npa_add_pin(p15card, npa_secret_name(PACE_PIN_ID_MRZ), -1,
SC_PKCS15_PIN_FLAG_CASE_SENSITIVE
| SC_PKCS15_PIN_FLAG_INITIALIZED
| SC_PKCS15_PIN_FLAG_SO_PIN
| SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED
| SC_PKCS15_PIN_FLAG_CHANGE_DISABLED,
90, 90, PACE_PIN_ID_MRZ, 0, mf, NULL, 0);
if (r != SC_SUCCESS)
goto err;
r = npa_add_pin(p15card, npa_secret_name(PACE_PIN_ID_CAN), -1,
SC_PKCS15_PIN_FLAG_CASE_SENSITIVE
| SC_PKCS15_PIN_FLAG_INITIALIZED
| SC_PKCS15_PIN_FLAG_SO_PIN
| SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED,
6, 6, PACE_PIN_ID_CAN, 0, mf, can, can ? strlen(can) : 0);
if (r != SC_SUCCESS)
goto err;
/* TODO */
/*r = sc_pkcs15_pincache_add(p15card, pin_obj, pinbuf, *pinsize);*/
r = npa_add_pin(p15card, npa_secret_name(PACE_PIN_ID_PIN), 3,
SC_PKCS15_PIN_FLAG_CASE_SENSITIVE
| SC_PKCS15_PIN_FLAG_INITIALIZED,
5, 6, PACE_PIN_ID_PIN, PACE_PIN_ID_PUK, mf, NULL, 0);
if (r != SC_SUCCESS)
goto err;
r = npa_add_pin(p15card, npa_secret_name(PACE_PIN_ID_PUK), -1,
SC_PKCS15_PIN_FLAG_CASE_SENSITIVE
| SC_PKCS15_PIN_FLAG_INITIALIZED
| SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN
| SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED
| SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN
| SC_PKCS15_PIN_FLAG_CHANGE_DISABLED,
10, 10, PACE_PIN_ID_PUK, 0, mf, NULL, 0);
if (r != SC_SUCCESS)
goto err;
r = npa_add_pin(p15card, "eSign PIN", 3,
SC_PKCS15_PIN_FLAG_CASE_SENSITIVE
| SC_PKCS15_PIN_FLAG_LOCAL
| SC_PKCS15_PIN_FLAG_INTEGRITY_PROTECTED
| SC_PKCS15_PIN_FLAG_INITIALIZED,
6, 6, NPA_PIN_ID_ESIGN_PIN, PACE_PIN_ID_PUK, &df_esign, NULL, 0);
if (r != SC_SUCCESS)
goto err;
err:
return r;
}
static const char npa_manufacturer[] = "Bundesdruckerei GmbH";
static int npa_add_cardlabels(sc_pkcs15_card_t *p15card)
{
if (!p15card || !p15card->tokeninfo)
return SC_ERROR_INTERNAL;
/* manufacturer ID */
free(p15card->tokeninfo->manufacturer_id);
p15card->tokeninfo->manufacturer_id = strdup(npa_manufacturer);
if (!p15card->tokeninfo->manufacturer_id)
return SC_ERROR_NOT_ENOUGH_MEMORY;
/* card label */
free(p15card->tokeninfo->label);
if (p15card->card && p15card->card->name) {
p15card->tokeninfo->label = strdup(p15card->card->name);
if (!p15card->tokeninfo->label)
return SC_ERROR_NOT_ENOUGH_MEMORY;
} else {
p15card->tokeninfo->label = NULL;
}
return SC_SUCCESS;
}
static int npa_get_cert(sc_pkcs15_card_t *p15card, const char *can)
{
struct establish_pace_channel_input pace_input;
struct establish_pace_channel_output pace_output;
int r;
if (!p15card || !p15card->card || !p15card->card->reader) {
r = SC_ERROR_INTERNAL;
goto err;
}
if (!(p15card->card->reader->capabilities & SC_READER_CAP_PACE_ESIGN)) {
sc_log(p15card->card->ctx, "No comfort reader (CAT-K) found\n");
r = SC_ERROR_NOT_SUPPORTED;
goto err;
}
memset(&pace_input, 0, sizeof pace_input);
memset(&pace_output, 0, sizeof pace_output);
pace_input.chat = esign_chat;
pace_input.chat_length = sizeof esign_chat;
pace_input.pin_id = PACE_PIN_ID_CAN;
if (can) {
pace_input.pin = (const unsigned char *) can;
pace_input.pin_length = strlen(can);
}
r = perform_pace(p15card->card, pace_input, &pace_output, EAC_TR_VERSION_2_02);
if (r != SC_SUCCESS)
goto err;
/* TODO read certificate */
r = SC_ERROR_OBJECT_NOT_FOUND;
err:
return r;
}
int sc_pkcs15emu_npa_init_ex(sc_pkcs15_card_t *p15card,
sc_pkcs15emu_opt_t *opts)
{
int r;
const char *can = NULL;
if (!p15card || !p15card->card) {
r = SC_ERROR_INTERNAL;
goto err;
}
if (opts && (opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)) {
/* don't do a card check */
} else {
r = npa_detect_card(p15card);
if (r != SC_SUCCESS)
goto err;
}
r = npa_add_cardlabels(p15card);
if (r != SC_SUCCESS)
goto err;
if (opts) {
can = scconf_get_str(opts->blk, "can", NULL);
}
r = npa_add_pins(p15card, can);
if (r != SC_SUCCESS)
goto err;
r = npa_get_cert(p15card, can);
if (r != SC_SUCCESS) {
sc_log(p15card->card->ctx, "No certificate found, will continue anyway\n");
r = SC_SUCCESS;
}
err:
return r;
}
const char *sc_driver_version(void)
{
/** TODO fix the version check in opensc/src/libopensc/pkcs15-syn.c:271
* here we choose 0.9.3 simply to pass the bogus check, see
* https://github.com/OpenSC/OpenSC/pull/258 */
static const char version[] = "0.9.3";
return version;
}

View File

@@ -297,3 +297,41 @@ int write_binary_rec(sc_card_t *card, unsigned char sfid,
err:
return r;
}
int fread_to_eof(const char *file, unsigned char **buf, size_t *buflen)
{
FILE *input = NULL;
int r = 0;
unsigned char *p;
if (!buflen || !buf)
goto err;
#define MAX_READ_LEN 0xfff
p = realloc(*buf, MAX_READ_LEN);
if (!p)
goto err;
*buf = p;
input = fopen(file, "rb");
if (!input) {
fprintf(stderr, "Could not open %s.\n", file);
goto err;
}
*buflen = 0;
while (feof(input) == 0 && *buflen < MAX_READ_LEN) {
*buflen += fread(*buf+*buflen, 1, MAX_READ_LEN-*buflen, input);
if (ferror(input)) {
fprintf(stderr, "Could not read %s.\n", file);
goto err;
}
}
r = 1;
err:
if (input)
fclose(input);
return r;
}