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

@@ -196,7 +196,7 @@ components of OpenSC. Load it by setting :envvar:`OPENSC_CONF`::
export OPENSC_CONF=$VSMARTCARD/npa/opensc.conf export OPENSC_CONF=$VSMARTCARD/npa/opensc.conf
On Windows you need to use :command:`set` instead of :command:`export`. In On Windows you need to use :command:`set` instead of :command:`export`. In
:file:`npa-0.6_win32` do the following:: :file:`npa-0.7_win32` do the following::
cd bin cd bin
set OPENSC_CONF=..\etc\opensc.conf set OPENSC_CONF=..\etc\opensc.conf
@@ -211,15 +211,6 @@ with :command:`opensc-explorer`::
OpenSC [3F00]> verify CHV3 313233343536 OpenSC [3F00]> verify CHV3 313233343536
Code correct. Code correct.
Since the German ID card does not contain any PKCS#15 data we emulate the data
structures with an other external library. Currently, the PKCS#15 emulator does
not support private key and certificate objects for qualified electronic
signature but only the PIN objects. For example, you can change the eID-PIN
using the :command:`pkcs15-tool`::
pkcs15-tool --change-pin --auth-id 03 \
--pin=123456 --new-pin=abcdef # yes, an ASCII eID-PIN is allowed
When the eID-PIN was verified incorrectly three times, it is blocked and must When the eID-PIN was verified incorrectly three times, it is blocked and must
be unblocked with the PUK. But unlike traditional cards the German ID card does be unblocked with the PUK. But unlike traditional cards the German ID card does
suspend the eID-PIN after the second try of verification. To unlock the last suspend the eID-PIN after the second try of verification. To unlock the last
@@ -227,7 +218,16 @@ retry, the CAN is required. Since the suspended state is not captured by
OpenSC we handle it transparently within the driver. If the eID-PIN shall be OpenSC we handle it transparently within the driver. If the eID-PIN shall be
verified and it is suspended, the card driver will verify the CAN first. If no verified and it is suspended, the card driver will verify the CAN first. If no
CAN is given in :file:`opensc.conf`, the driver will request it on the standard CAN is given in :file:`opensc.conf`, the driver will request it on the standard
input. input. You can use |npa-tool| to unblock or resume the eID-PIN.
The German ID card is capable of creating a qualified electronic signature.
Therefor, the card must be initialized by a trust center with the user's
consent. Today this means, that the user need to register for `sign-me`_. The
process also initializes the QES-PIN, which unlocks the signature key. Below,
you can see two examples how to create a signature::
pkcs15-crypt --sign --sha-256 --input $SHA256_FILE --pin $QES_PIN
pkcs11-tool --module opensc-pkcs11.so --sign --pin $QES_PIN --input-file $SHA256_FILE
.. include:: questions.txt .. include:: questions.txt
@@ -242,3 +242,4 @@ Notes and References
.. _OpenPACE: https://frankmorgner.github.io/openpace/ .. _OpenPACE: https://frankmorgner.github.io/openpace/
.. _OpenSC: https://github.com/OpenSC/OpenSC .. _OpenSC: https://github.com/OpenSC/OpenSC
.. _OpenSSL: http://www.openssl.org .. _OpenSSL: http://www.openssl.org
.. _sign-me: https://www.bundesdruckerei.de/en/798-sign-me

View File

@@ -211,15 +211,6 @@ with :command:`opensc-explorer`::
OpenSC [3F00]> verify CHV3 313233343536 OpenSC [3F00]> verify CHV3 313233343536
Code correct. Code correct.
Since the German ID card does not contain any PKCS#15 data we emulate the data
structures with an other external library. Currently, the PKCS#15 emulator does
not support private key and certificate objects for qualified electronic
signature but only the PIN objects. For example, you can change the eID-PIN
using the :command:`pkcs15-tool`::
pkcs15-tool --change-pin --auth-id 03 \
--pin=123456 --new-pin=abcdef # yes, an ASCII eID-PIN is allowed
When the eID-PIN was verified incorrectly three times, it is blocked and must When the eID-PIN was verified incorrectly three times, it is blocked and must
be unblocked with the PUK. But unlike traditional cards the German ID card does be unblocked with the PUK. But unlike traditional cards the German ID card does
suspend the eID-PIN after the second try of verification. To unlock the last suspend the eID-PIN after the second try of verification. To unlock the last
@@ -227,7 +218,16 @@ retry, the CAN is required. Since the suspended state is not captured by
OpenSC we handle it transparently within the driver. If the eID-PIN shall be OpenSC we handle it transparently within the driver. If the eID-PIN shall be
verified and it is suspended, the card driver will verify the CAN first. If no verified and it is suspended, the card driver will verify the CAN first. If no
CAN is given in :file:`opensc.conf`, the driver will request it on the standard CAN is given in :file:`opensc.conf`, the driver will request it on the standard
input. input. You can use |npa-tool| to unblock or resume the eID-PIN.
The German ID card is capable of creating a qualified electronic signature.
Therefor, the card must be initialized by a trust center with the user's
consent. Today this means, that the user need to register for `sign-me`_. The
process also initializes the QES-PIN, which unlocks the signature key. Below,
you can see two examples how to create a signature::
pkcs15-crypt --sign --sha-256 --input $SHA256_FILE --pin $QES_PIN
pkcs11-tool --module opensc-pkcs11.so --sign --pin $QES_PIN --input-file $SHA256_FILE
.. include:: questions.txt .. include:: questions.txt
@@ -242,3 +242,4 @@ Notes and References
.. _OpenPACE: https://frankmorgner.github.io/openpace/ .. _OpenPACE: https://frankmorgner.github.io/openpace/
.. _OpenSC: https://github.com/OpenSC/OpenSC .. _OpenSC: https://github.com/OpenSC/OpenSC
.. _OpenSSL: http://www.openssl.org .. _OpenSSL: http://www.openssl.org
.. _sign-me: https://www.bundesdruckerei.de/en/798-sign-me

View File

@@ -19,23 +19,22 @@ app default {
card_driver npa { card_driver npa {
# The location of the driver library # The location of the driver library
module = @libdir@/libcardnpa.@DYN_LIB_EXT@; module = @libdir@/libcardnpa.@DYN_LIB_EXT@;
}
# German ID card requires the CAN to be verified before QES PIN. This,
# however, is not part of the PKCS#15 profile of the card. So for
# verifying the PIN we actually need both. The CAN may be given here.
# If the CAN is not given here, it will be prompted on the command line
# or on the reader.
can = 222222;
# PKCS #15 # QES is only possible with a Comfort Reader (CAT-K), which holds a
framework pkcs15 { # cryptographic key to authenticate itself as signature terminal (ST).
# additional settings per driver # We usually will use the reader's capability to sign the data.
# # However, during developement you may specify soft certificates and
# For pkcs15 emulators loaded from an external shared # keys for a ST below.
# library/DLL, you need to specify the path name of the module # The following example EAC PKI can be found in npa-example-data
# and customize the card_atr example above correctly. st_dv_certificate = ZZSTDVCA00001.cvcert;
# st_certificate = ZZSTTERM00001.cvcert;
emulate npa { st_key = ZZSTTERM00001.pkcs8;
# The location of the driver library
module = @libdir@/libpkcs15npa.@DYN_LIB_EXT@;
function = sc_pkcs15emu_npa_init_ex;
can = 222222;
}
} }
} }

View File

@@ -27,11 +27,6 @@ libcardnpa_la_LIBADD = $(OPENSC_LIBS) libnpa.la
libcardnpa_la_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS) libcardnpa_la_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS)
libcardnpa_la_LDFLAGS = -no-undefined 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_SOURCES = npa-tool.c $(BUILT_SOURCES)
npa_tool_LDADD = libnpa.la $(OPENSC_LIBS) $(OPENPACE_LIBS) $(OPENSSL_LIBS) npa_tool_LDADD = libnpa.la $(OPENSC_LIBS) $(OPENPACE_LIBS) $(OPENSSL_LIBS)
npa_tool_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS) 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 bin_PROGRAMS = npa-tool
noinst_PROGRAMS = example noinst_PROGRAMS = example
lib_LTLIBRARIES = libnpa.la libcardnpa.la libpkcs15npa.la lib_LTLIBRARIES = libnpa.la libcardnpa.la
noinst_HEADERS = \ noinst_HEADERS = \
sslutil.h \ sslutil.h \

View File

@@ -24,12 +24,54 @@
#include "card-npa.h" #include "card-npa.h"
#include <npa/boxing.h> #include <npa/boxing.h>
#include <npa/npa.h> #include <npa/npa.h>
#include <npa/scutil.h>
#include <string.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[] = { 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:8A:80:01:80:31:F8:73:F7:41:E0:82:90:00:75",
{"3B:84:80:01:00:00:90:00:95", NULL, "German ID card (Test neuer Personalausweis)", SC_CARD_TYPE_NPA_TEST, 0, NULL}, "FF:FF:FF:FF:FF:FF:00:FF:00:00:FF:FF:FF:FF:00",
{"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}, "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} {NULL, NULL, NULL, 0, 0, NULL}
}; };
@@ -41,6 +83,64 @@ static struct sc_card_driver npa_drv = {
NULL, 0, NULL 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) static int npa_match_card(sc_card_t * card)
{ {
if (_sc_match_atr(card, npa_atrs, &card->type) < 0) 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; 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) { struct npa_drv_data *drv_data;
#if 0
/* we wait for https://github.com/OpenSC/OpenSC/pull/260 to be if (card->drv_data) {
* integrated before switching extended length on, here */ drv_data = card->drv_data;
card->max_recv_size = 0xFFFF+1;
card->max_send_size = 0xFFFF; if (pace_output) {
#endif pace_output->ef_cardaccess = drv_data->ef_cardaccess;
card->caps |= SC_CARD_CAP_APDU_EXT | SC_CARD_CAP_RNG; pace_output->ef_cardaccess_length = drv_data->ef_cardaccess_length;
memset(&card->sm_ctx, 0, sizeof card->sm_ctx); }
#ifdef DISABLE_GLOBAL_BOXING_INITIALIZATION
sc_detect_boxing_cmds(card->reader); if (pace_input && pace_input->pin_id == PACE_PIN_ID_CAN) {
#endif 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) static int npa_finish(sc_card_t * card)
{ {
sm_stop(card); sm_stop(card);
npa_drv_data_free(card->drv_data);
card->drv_data = NULL;
EAC_cleanup();
return SC_SUCCESS; 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, static int npa_pin_cmd_get_info(struct sc_card *card,
struct sc_pin_cmd_data *data, int *tries_left) 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 = pin->data;
pace_input.pin_length = pin->len; 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); 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.recent_car);
free(pace_output.previous_car); free(pace_output.previous_car);
free(pace_output.id_icc); free(pace_output.id_icc);
@@ -281,27 +618,17 @@ static int npa_pin_cmd(struct sc_card *card,
goto err; goto err;
break; break;
case NPA_PIN_ID_ESIGN_PIN: default:
if (card->reader->capabilities & SC_READER_CAP_PACE_ESIGN) { /* assuming QES PIN */
sc_log(card->ctx, "Found a comfort reader (CAT-K).\n");
sc_log(card->ctx, "Will verify CAN first.\n"); /* We assume that the eSign application has already been
r = npa_pace_verify(card, PACE_PIN_ID_CAN, NULL, * unlocked, see npa_init().
esign_chat, sizeof esign_chat, tries_left); *
if (r != SC_SUCCESS) * Now, verify the QES PIN. */
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");
}
r = npa_standard_pin_cmd(card, data, tries_left); r = npa_standard_pin_cmd(card, data, tries_left);
if (r != SC_SUCCESS) if (r != SC_SUCCESS)
goto err; goto err;
break; break;
default:
r = SC_ERROR_OBJECT_NOT_FOUND;
goto err;
break;
} }
if (data->cmd == SC_PIN_CMD_CHANGE) { 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.match_card = npa_match_card;
npa_ops.init = npa_init; npa_ops.init = npa_init;
npa_ops.finish = npa_finish; npa_ops.finish = npa_finish;
npa_ops.set_security_env = npa_set_security_env;
npa_ops.pin_cmd = npa_pin_cmd; npa_ops.pin_cmd = npa_pin_cmd;
npa_ops.logout = npa_logout; npa_ops.logout = npa_logout;

View File

@@ -32,8 +32,6 @@ enum {
SC_CARD_TYPE_NPA_ONLINE, SC_CARD_TYPE_NPA_ONLINE,
}; };
#define NPA_PIN_ID_ESIGN_PIN 0x83
const unsigned char esign_chat[] = { const unsigned char esign_chat[] = {
0x7F, 0x4C, 0x0E, 0x7F, 0x4C, 0x0E,
0x06, 0x09, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x03, 0x01, 0x02, 0x03, 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 #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, static void read_dg(sc_card_t *card, unsigned char sfid, const char *dg_str,
unsigned char **dg, size_t *dg_len) unsigned char **dg, size_t *dg_len)
{ {
@@ -299,6 +261,8 @@ main (int argc, char **argv)
unsigned char *dg = NULL; unsigned char *dg = NULL;
size_t dg_len = 0; size_t dg_len = 0;
ASN1_AUXILIARY_DATA *templates = NULL; ASN1_AUXILIARY_DATA *templates = NULL;
unsigned char *ef_cardsecurity = NULL;
size_t ef_cardsecurity_len = 0;
struct gengetopt_args_info cmdline; struct gengetopt_args_info cmdline;
@@ -695,7 +659,7 @@ nopace:
goto err; goto err;
printf("Performed Terminal Authentication.\n"); printf("Performed Terminal Authentication.\n");
r = perform_chip_authentication(card); r = perform_chip_authentication(card, &ef_cardsecurity, &ef_cardsecurity_len);
if (r < 0) if (r < 0)
goto err; goto err;
printf("Performed Chip Authentication.\n"); printf("Performed Chip Authentication.\n");
@@ -803,6 +767,10 @@ err:
free(pace_output.previous_car); free(pace_output.previous_car);
free(pace_output.id_icc); free(pace_output.id_icc);
free(pace_output.id_pcd); free(pace_output.id_pcd);
if (ef_cardsecurity) {
OPENSSL_cleanse(ef_cardsecurity, ef_cardsecurity_len);
free(ef_cardsecurity);
}
if (input) if (input)
fclose(input); fclose(input);
if (certs) { 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); 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; int r;
BUF_MEM *picc_pubkey = NULL, *nonce = NULL, *token = NULL, BUF_MEM *picc_pubkey = NULL, *nonce = NULL, *token = NULL,
*eph_pub_key = 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; r = SC_ERROR_INVALID_ARGUMENTS;
goto err; goto err;
} }
@@ -1863,12 +1863,14 @@ int perform_chip_authentication(sc_card_t *card)
/* Passive Authentication */ /* Passive Authentication */
r = get_ef_card_security(card, &ef_cardsecurity, &ef_cardsecurity_len); if (!*ef_cardsecurity && !*ef_cardsecurity_len) {
if (r < 0) { r = get_ef_card_security(card, ef_cardsecurity, ef_cardsecurity_len);
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not get EF.CardSecurity."); if (r < 0 || !ef_cardsecurity || !ef_cardsecurity_len) {
goto err; 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) { if (!picc_pubkey) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not verify EF.CardSecurity."); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not verify EF.CardSecurity.");
ssl_error(card->ctx); ssl_error(card->ctx);
@@ -1924,10 +1926,6 @@ int perform_chip_authentication(sc_card_t *card)
} }
err: err:
if (ef_cardsecurity) {
OPENSSL_cleanse(ef_cardsecurity, ef_cardsecurity_len);
free(ef_cardsecurity);
}
BUF_MEM_clear_free(picc_pubkey); BUF_MEM_clear_free(picc_pubkey);
BUF_MEM_clear_free(nonce); BUF_MEM_clear_free(nonce);
BUF_MEM_clear_free(token); 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. * Switches the SM context of \c card to the new established keys.
* *
* @param[in] card * @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 * @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 * @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. */ * be null terminated. */
int _sc_match_atr(struct sc_card *card, struct sc_atr_table *table, int *type_out); 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 #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: err:
return r; 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;
}

Binary file not shown.

View File

@@ -0,0 +1,8 @@
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBAQ==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MF8CAQEEGLxabXaKHuZ+jf1t1sK4rVH1ovP+Bm7Es6AKBggqhkjOPQMBAaE0AzIA
BKzacemlIv9Y+FIrsRH/Cgue5XsIdy+CmEudCTHDJ0TeqpJdQyuFSbBCy0w3BkjC
qw==
-----END EC PRIVATE KEY-----

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -97,6 +97,14 @@ For Example: GivenNames=GERTRUD. \
The following Dataset Elements may be used in the dataset file: \ The following Dataset Elements may be used in the dataset file: \
DocumentType, IssuingState, DateOfExpiry, GivenNames, FamilyNames, ReligiousArtisticName, AcademicTitle, DateOfBirth, PlaceOfBirth, Nationality, Sex, BirthName, Country, City, ZIP, Street, CommunityID, ResidencePermit1, ResidencePermit2, dg12, dg14, dg15, dg16, dg21.\ DocumentType, IssuingState, DateOfExpiry, GivenNames, FamilyNames, ReligiousArtisticName, AcademicTitle, DateOfBirth, PlaceOfBirth, Nationality, Sex, BirthName, Country, City, ZIP, Street, CommunityID, ResidencePermit1, ResidencePermit2, dg12, dg14, dg15, dg16, dg21.\
") ")
npa.add_argument("--esign-cert",
action="store",
type=argparse.FileType('rb'),
help="the card holder's certificate for QES")
npa.add_argument("--esign-ca-cert",
action="store",
type=argparse.FileType('rb'),
help="the CA's certificate for QES")
args = parser.parse_args() args = parser.parse_args()
@@ -107,6 +115,8 @@ ef_cardsecurity_data = None
ef_cardaccess_data = None ef_cardaccess_data = None
ca_key_data = None ca_key_data = None
cvca = None cvca = None
esign_ca_cert = None
esign_cert = None
if (args.ef_cardsecurity): if (args.ef_cardsecurity):
ef_cardsecurity_data = args.ef_cardsecurity.read() ef_cardsecurity_data = args.ef_cardsecurity.read()
args.ef_cardsecurity.close() args.ef_cardsecurity.close()
@@ -119,6 +129,12 @@ if (args.ca_key):
if (args.cvca): if (args.cvca):
cvca = args.cvca.read() cvca = args.cvca.read()
args.cvca.close() args.cvca.close()
if (args.esign_cert):
esign_cert = args.esign_cert.read()
args.esign_cert.close()
if (args.esign_ca_cert):
esign_ca_cert = args.esign_ca_cert.read()
args.esign_ca_cert.close()
if not args.verbose: if not args.verbose:
logginglevel = logging.CRITICAL logginglevel = logging.CRITICAL
@@ -139,5 +155,7 @@ else:
vicc = VirtualICC(args.file, args.datasetfile, args.type, vicc = VirtualICC(args.file, args.datasetfile, args.type,
hostname, args.port, readernum=args.reader, hostname, args.port, readernum=args.reader,
ef_cardaccess=ef_cardaccess_data, ef_cardsecurity=ef_cardsecurity_data, ef_cardaccess=ef_cardaccess_data, ef_cardsecurity=ef_cardsecurity_data,
ca_key=ca_key_data, cvca=cvca, disable_checks=args.disable_ta_checks, logginglevel=logginglevel) ca_key=ca_key_data, cvca=cvca, disable_checks=args.disable_ta_checks,
esign_ca_cert=esign_ca_cert, esign_cert=esign_cert,
logginglevel=logginglevel)
vicc.run() vicc.run()

View File

@@ -341,7 +341,31 @@ class CardGenerator(object):
eid.append(TransparentStructureEF(parent=eid, fid=0x0115, shortfid=0x15, data=dg21)) eid.append(TransparentStructureEF(parent=eid, fid=0x0115, shortfid=0x15, data=dg21))
self.mf.append(eid) self.mf.append(eid)
self.sam = nPA_SAM(pin="111111", can="222222", mrz="IDD<<T220001293<<<<<<<<<<<<<<<6408125<2010315D<<<<<<<<<<<<<<MUSTERMANN<<ERIKA<<<<<<<<<<<<<", puk="3333333333", mf=self.mf)
# DF.CIA
cia=DF(parent=self.mf, fid=0xfffe, dfname='\xE8\x28\xBD\x08\x0F\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E')
# EF.OD / EF.ODF
cia.append(TransparentStructureEF(parent=cia, fid=0x5031, shortfid=0x11, data='\xa0\x060\x04\x04\x02D\x00\xa4\x060\x04\x04\x02D\x04\xa8\x060\x04\x04\x02D\x08'))
# EF.CIAInfo / EF.TokenInfo
cia.append(TransparentStructureEF(parent=cia, fid=0x5032, shortfid=0x12, data='06\x02\x01\x01\x80\x11eSign Application\x03\x02\x06\xc0\xa2\x1a0\x18\x02\x01\x01\x02\x02\x10A\x05\x00\x03\x02\x06@\x06\t\x04\x00\x7f\x00\x07\x01\x01\x04\x01'))
# EF.PrKD / EF.PrKDF
cia.append(TransparentStructureEF(parent=cia, fid=0x4400, data='\xa080\x17\x0c\x0bPrK.ICC.QES\x03\x02\x07\x80\x04\x01\x01\x02\x01\x010\x15\x04\x01F\x03\x03\x06\x00@\x03\x02\x03\xb8\x02\x02\x00\x84\xa1\x03\x02\x01\x01\xa1\x060\x040\x02\x04\x00'))
# EF.PuKD / EF.PuKDF
cia.append(TransparentStructureEF(parent=cia, fid=0x4404, data='050!\x0c\x1fZertifikat des ZDA f\xc3\xbcr die QES0\x06\x04\x01E\x01\x01\xff\xa1\x080\x060\x04\x04\x02\xc0\x000:0&\x0c$Zertifikat des Inhabers f\xc3\xbcr die QES0\x06\x04\x01F\x01\x01\x00\xa1\x080\x060\x04\x04\x02\xc0\x01'))
# EF.AOD / EF.AODF
cia.append(TransparentStructureEF(parent=cia, fid=0x4408, data='0/0\x0f\x0c\teSign-PIN\x03\x02\x06@0\x03\x04\x01\x01\xa1\x170\x15\x03\x03\x02H\x1c\n\x01\x01\x02\x01\x06\x02\x01\x00\x80\x01\x810\x02\x04\x00'))
self.mf.append(cia)
# DF.eSign
esign=DF(parent=self.mf, fid=0xfffd, dfname='\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E')
# ZDA certificate
esign.append(TransparentStructureEF(parent=esign, fid=0xC000, data=''))
# User's certificate
esign.append(TransparentStructureEF(parent=esign, fid=0xC001, data=''))
self.mf.append(esign)
self.sam = nPA_SAM(eid_pin="111111", can="222222", mrz="IDD<<T220001293<<<<<<<<<<<<<<<6408125<2010315D<<<<<<<<<<<<<<MUSTERMANN<<ERIKA<<<<<<<<<<<<<", puk="3333333333", qes_pin="444444", mf=self.mf)
# FIXME add CVCA for inspection systems and signature terminals. Here we only add the eID CVCA. # FIXME add CVCA for inspection systems and signature terminals. Here we only add the eID CVCA.
self.sam.current_SE.cvca = '\x7f\x21\x82\x01\xb6\x7f\x4e\x82\x01\x6e\x5f\x29\x01\x00\x42\x0e\x44\x45\x43\x56\x43\x41\x65\x49\x44\x30\x30\x31\x30\x32\x7f\x49\x82\x01\x1d\x06\x0a\x04\x00\x7f\x00\x07\x02\x02\x02\x02\x03\x81\x20\xa9\xfb\x57\xdb\xa1\xee\xa9\xbc\x3e\x66\x0a\x90\x9d\x83\x8d\x72\x6e\x3b\xf6\x23\xd5\x26\x20\x28\x20\x13\x48\x1d\x1f\x6e\x53\x77\x82\x20\x7d\x5a\x09\x75\xfc\x2c\x30\x57\xee\xf6\x75\x30\x41\x7a\xff\xe7\xfb\x80\x55\xc1\x26\xdc\x5c\x6c\xe9\x4a\x4b\x44\xf3\x30\xb5\xd9\x83\x20\x26\xdc\x5c\x6c\xe9\x4a\x4b\x44\xf3\x30\xb5\xd9\xbb\xd7\x7c\xbf\x95\x84\x16\x29\x5c\xf7\xe1\xce\x6b\xcc\xdc\x18\xff\x8c\x07\xb6\x84\x41\x04\x8b\xd2\xae\xb9\xcb\x7e\x57\xcb\x2c\x4b\x48\x2f\xfc\x81\xb7\xaf\xb9\xde\x27\xe1\xe3\xbd\x23\xc2\x3a\x44\x53\xbd\x9a\xce\x32\x62\x54\x7e\xf8\x35\xc3\xda\xc4\xfd\x97\xf8\x46\x1a\x14\x61\x1d\xc9\xc2\x77\x45\x13\x2d\xed\x8e\x54\x5c\x1d\x54\xc7\x2f\x04\x69\x97\x85\x20\xa9\xfb\x57\xdb\xa1\xee\xa9\xbc\x3e\x66\x0a\x90\x9d\x83\x8d\x71\x8c\x39\x7a\xa3\xb5\x61\xa6\xf7\x90\x1e\x0e\x82\x97\x48\x56\xa7\x86\x41\x04\x33\x47\xec\xf9\x6f\xfb\x4b\xd9\xb8\x55\x4e\xfb\xcc\xfc\x7d\x0b\x24\x2f\x10\x71\xe2\x9b\x4c\x9c\x62\x2c\x79\xe3\x39\xd8\x40\xaf\x67\xbe\xb9\xb9\x12\x69\x22\x65\xd9\xc1\x6c\x62\x57\x3f\x45\x79\xff\xd4\xde\x2d\xe9\x2b\xab\x40\x9d\xd5\xc5\xd4\x82\x44\xa9\xf7\x87\x01\x01\x5f\x20\x0e\x44\x45\x43\x56\x43\x41\x65\x49\x44\x30\x30\x31\x30\x32\x7f\x4c\x12\x06\x09\x04\x00\x7f\x00\x07\x03\x01\x02\x02\x53\x05\xfe\x0f\x01\xff\xff\x5f\x25\x06\x01\x00\x01\x00\x01\x08\x5f\x24\x06\x01\x03\x01\x00\x01\x08\x5f\x37\x40\x50\x67\x14\x5c\x68\xca\xe9\x52\x0f\x5b\xb3\x48\x17\xf1\xca\x9c\x43\x59\x3d\xb5\x64\x06\xc6\xa3\xb0\x06\xcb\xf3\xf3\x14\xe7\x34\x9a\xcf\x0c\xc6\xbf\xeb\xcb\xde\xfd\x10\xb4\xdc\xf0\xf2\x31\xda\x56\x97\x7d\x88\xf9\xf9\x01\x82\xd1\x99\x07\x6a\x56\x50\x64\x51' self.sam.current_SE.cvca = '\x7f\x21\x82\x01\xb6\x7f\x4e\x82\x01\x6e\x5f\x29\x01\x00\x42\x0e\x44\x45\x43\x56\x43\x41\x65\x49\x44\x30\x30\x31\x30\x32\x7f\x49\x82\x01\x1d\x06\x0a\x04\x00\x7f\x00\x07\x02\x02\x02\x02\x03\x81\x20\xa9\xfb\x57\xdb\xa1\xee\xa9\xbc\x3e\x66\x0a\x90\x9d\x83\x8d\x72\x6e\x3b\xf6\x23\xd5\x26\x20\x28\x20\x13\x48\x1d\x1f\x6e\x53\x77\x82\x20\x7d\x5a\x09\x75\xfc\x2c\x30\x57\xee\xf6\x75\x30\x41\x7a\xff\xe7\xfb\x80\x55\xc1\x26\xdc\x5c\x6c\xe9\x4a\x4b\x44\xf3\x30\xb5\xd9\x83\x20\x26\xdc\x5c\x6c\xe9\x4a\x4b\x44\xf3\x30\xb5\xd9\xbb\xd7\x7c\xbf\x95\x84\x16\x29\x5c\xf7\xe1\xce\x6b\xcc\xdc\x18\xff\x8c\x07\xb6\x84\x41\x04\x8b\xd2\xae\xb9\xcb\x7e\x57\xcb\x2c\x4b\x48\x2f\xfc\x81\xb7\xaf\xb9\xde\x27\xe1\xe3\xbd\x23\xc2\x3a\x44\x53\xbd\x9a\xce\x32\x62\x54\x7e\xf8\x35\xc3\xda\xc4\xfd\x97\xf8\x46\x1a\x14\x61\x1d\xc9\xc2\x77\x45\x13\x2d\xed\x8e\x54\x5c\x1d\x54\xc7\x2f\x04\x69\x97\x85\x20\xa9\xfb\x57\xdb\xa1\xee\xa9\xbc\x3e\x66\x0a\x90\x9d\x83\x8d\x71\x8c\x39\x7a\xa3\xb5\x61\xa6\xf7\x90\x1e\x0e\x82\x97\x48\x56\xa7\x86\x41\x04\x33\x47\xec\xf9\x6f\xfb\x4b\xd9\xb8\x55\x4e\xfb\xcc\xfc\x7d\x0b\x24\x2f\x10\x71\xe2\x9b\x4c\x9c\x62\x2c\x79\xe3\x39\xd8\x40\xaf\x67\xbe\xb9\xb9\x12\x69\x22\x65\xd9\xc1\x6c\x62\x57\x3f\x45\x79\xff\xd4\xde\x2d\xe9\x2b\xab\x40\x9d\xd5\xc5\xd4\x82\x44\xa9\xf7\x87\x01\x01\x5f\x20\x0e\x44\x45\x43\x56\x43\x41\x65\x49\x44\x30\x30\x31\x30\x32\x7f\x4c\x12\x06\x09\x04\x00\x7f\x00\x07\x03\x01\x02\x02\x53\x05\xfe\x0f\x01\xff\xff\x5f\x25\x06\x01\x00\x01\x00\x01\x08\x5f\x24\x06\x01\x03\x01\x00\x01\x08\x5f\x37\x40\x50\x67\x14\x5c\x68\xca\xe9\x52\x0f\x5b\xb3\x48\x17\xf1\xca\x9c\x43\x59\x3d\xb5\x64\x06\xc6\xa3\xb0\x06\xcb\xf3\xf3\x14\xe7\x34\x9a\xcf\x0c\xc6\xbf\xeb\xcb\xde\xfd\x10\xb4\xdc\xf0\xf2\x31\xda\x56\x97\x7d\x88\xf9\xf9\x01\x82\xd1\x99\x07\x6a\x56\x50\x64\x51'

View File

@@ -503,7 +503,7 @@ class RelayOS(SmartcardOS):
class NPAOS(Iso7816OS): class NPAOS(Iso7816OS):
def __init__(self, mf, sam, ins2handler=None, maxle=MAX_EXTENDED_LE, ef_cardsecurity=None, ef_cardaccess=None, ca_key=None, cvca=None, disable_checks=False): def __init__(self, mf, sam, ins2handler=None, maxle=MAX_EXTENDED_LE, ef_cardsecurity=None, ef_cardaccess=None, ca_key=None, cvca=None, disable_checks=False, esign_key=None, esign_ca_cert=None, esign_cert=None):
Iso7816OS.__init__(self, mf, sam, ins2handler, maxle) Iso7816OS.__init__(self, mf, sam, ins2handler, maxle)
self.ins2handler[0x86] = self.SAM.general_authenticate self.ins2handler[0x86] = self.SAM.general_authenticate
self.ins2handler[0x2c] = self.SAM.reset_retry_counter self.ins2handler[0x2c] = self.SAM.reset_retry_counter
@@ -526,6 +526,13 @@ class NPAOS(Iso7816OS):
self.SAM.current_SE.cvca = cvca self.SAM.current_SE.cvca = cvca
if ca_key: if ca_key:
self.SAM.current_SE.ca_key = ca_key self.SAM.current_SE.ca_key = ca_key
esign = self.mf.select('dfname', '\xA0\x00\x00\x01\x67\x45\x53\x49\x47\x4E')
if esign_ca_cert:
ef = esign.select('fid', 0xC000)
ef.data = esign_ca_cert
if esign_cert:
ef = esign.select('fid', 0xC001)
ef.data = esign_cert
def formatResult(self, seekable, le, data, sw, sm): def formatResult(self, seekable, le, data, sw, sm):
@@ -642,7 +649,7 @@ class VirtualICC(object):
the vpcd, which forwards it to the application. the vpcd, which forwards it to the application.
""" """
def __init__(self, filename, datasetfile, card_type, host, port, readernum=None, ef_cardsecurity=None, ef_cardaccess=None, ca_key=None, cvca=None, disable_checks=False, logginglevel=logging.INFO): def __init__(self, filename, datasetfile, card_type, host, port, readernum=None, ef_cardsecurity=None, ef_cardaccess=None, ca_key=None, cvca=None, disable_checks=False, esign_key=None, esign_ca_cert=None, esign_cert=None, logginglevel=logging.INFO):
from os.path import exists from os.path import exists
logging.basicConfig(level = logginglevel, logging.basicConfig(level = logginglevel,
@@ -674,7 +681,7 @@ class VirtualICC(object):
if card_type == "iso7816" or card_type == "ePass": if card_type == "iso7816" or card_type == "ePass":
self.os = Iso7816OS(MF, SAM) self.os = Iso7816OS(MF, SAM)
elif card_type == "nPA": elif card_type == "nPA":
self.os = NPAOS(MF, SAM, ef_cardsecurity=ef_cardsecurity, ef_cardaccess=ef_cardaccess, ca_key=ca_key, cvca=cvca, disable_checks=disable_checks) self.os = NPAOS(MF, SAM, ef_cardsecurity=ef_cardsecurity, ef_cardaccess=ef_cardaccess, ca_key=ca_key, cvca=cvca, disable_checks=disable_checks, esign_key=esign_key, esign_ca_cert=esign_ca_cert, esign_cert=esign_cert)
elif card_type == "cryptoflex": elif card_type == "cryptoflex":
self.os = CryptoflexOS(MF, SAM) self.os = CryptoflexOS(MF, SAM)
elif card_type == "relay": elif card_type == "relay":

View File

@@ -171,7 +171,7 @@ class nPA_SE(Security_Environment):
if self.sam.counter == 1 and not self.sam.active: if self.sam.counter == 1 and not self.sam.active:
print "Must use CAN to activate" print "Must use CAN to activate"
return 0x63c1, "" return 0x63c1, ""
self.PACE_SEC = PACE_SEC(self.sam.PIN, eac.PACE_PIN) self.PACE_SEC = PACE_SEC(self.sam.eid_pin, eac.PACE_PIN)
self.sam.counter -= 1 self.sam.counter -= 1
if self.sam.counter <= 1: if self.sam.counter <= 1:
self.sam.active = False self.sam.active = False
@@ -372,6 +372,7 @@ class nPA_SE(Security_Environment):
if 1 != eac.TA_STEP6_verify(self.eac_ctx, self.at.iv, id_picc, if 1 != eac.TA_STEP6_verify(self.eac_ctx, self.at.iv, id_picc,
auxiliary_data, data): auxiliary_data, data):
eac.print_ossl_err() eac.print_ossl_err()
print "Could not verify Terminal's signature"
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"]) raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
print "Terminal's signature verified" print "Terminal's signature verified"
@@ -449,13 +450,18 @@ class nPA_SE(Security_Environment):
return sw, return_data return sw, return_data
def compute_digital_signature(self, p1, p2, data):
# TODO Signing with brainpoolP256r1 or any other key needs some more effort ;-)
return '\x0D\xB2\x9B\xB9\x5E\x97\x7D\x42\x73\xCF\xA5\x45\xB7\xED\x5C\x39\x3F\xCE\xCD\x4A\xDE\xDC\x2B\x85\x23\x9F\x66\x52\x10\xC2\x67\xDC\xA6\x35\x94\x2D\x24\xED\xEB\xC8\x34\x6C\x4B\xD1\xA1\x15\xB4\x48\x3A\xA4\x4A\xCE\xFF\xED\x97\x0E\x07\xF3\x72\xF0\xFB\xA3\x62\x8C'
class nPA_SAM(SAM): class nPA_SAM(SAM):
def __init__(self, pin, can, mrz, puk, mf, default_se = nPA_SE): def __init__(self, eid_pin, can, mrz, puk, qes_pin, mf, default_se = nPA_SE):
SAM.__init__(self, pin, None, mf) SAM.__init__(self, qes_pin, None, mf)
self.active = True self.active = True
self.current_SE = default_se(self.mf, self) self.current_SE = default_se(self.mf, self)
self.eid_pin = eid_pin
self.can = can self.can = can
self.mrz = mrz self.mrz = mrz
self.puk = puk self.puk = puk
@@ -479,8 +485,8 @@ class nPA_SAM(SAM):
# TODO allow terminals to change the PIN with permission "CAN allowed" # TODO allow terminals to change the PIN with permission "CAN allowed"
if not self.current_SE.at.keyref_is_pin(): if not self.current_SE.at.keyref_is_pin():
raise SwError(SW["ERR_CONDITIONNOTSATISFIED"]) raise SwError(SW["ERR_CONDITIONNOTSATISFIED"])
self.PIN = data self.eid_pin = data
print "Changed PIN to %r" % self.PIN print "Changed PIN to %r" % self.eid_pin
else: else:
raise SwError(SW["ERR_DATANOTFOUND"]) raise SwError(SW["ERR_DATANOTFOUND"])
elif p1 == 0x03: elif p1 == 0x03:
@@ -528,26 +534,24 @@ class nPA_SAM(SAM):
return SW["NORMAL"], self.last_challenge return SW["NORMAL"], self.last_challenge
def verify(self, p1, p2, data): def verify(self, p1, p2, data):
if (p1 != 0x80 or p2 != 0x00): if p1 == 0x80 and p2 == 0x00:
raise SwError(SW["ERR_INCORRECTP1P2"]) if self.current_SE.eac_step == 6:
structure = unpack(data)
if self.current_SE.eac_step == 6: for tag, length, value in structure:
structure = unpack(data) if tag == 6 and ALGO_MAPPING[value] == "DateOfExpiry":
for tag, length, value in structure: # hell yes, this is a valid nPA
if tag == 6 and ALGO_MAPPING[value] == "DateOfExpiry": # TODO actually check it...
# hell yes, this is a valid nPA return SW["NORMAL"], ""
# TODO actually check it... if tag == 6 and ALGO_MAPPING[value] == "DateOfBirth":
return SW["NORMAL"], "" # hell yes, we are old enough
if tag == 6 and ALGO_MAPPING[value] == "DateOfBirth": # TODO actually check it...
# hell yes, we are old enough return SW["NORMAL"], ""
# TODO actually check it... if tag == 6 and ALGO_MAPPING[value] == "CommunityID":
return SW["NORMAL"], "" # well OK, we are living there
if tag == 6 and ALGO_MAPPING[value] == "CommunityID": # TODO actually check it...
# well OK, we are living there return SW["NORMAL"], ""
# TODO actually check it... else:
return SW["NORMAL"], "" return SAM.verify(self, p1, p2, data)
raise SwError(SW["WARN_NOINFO63"])
def parse_SM_CAPDU(self, CAPDU, header_authentication): def parse_SM_CAPDU(self, CAPDU, header_authentication):
if hasattr(self.current_SE, "new_encryption_ctx"): if hasattr(self.current_SE, "new_encryption_ctx"):