Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 \
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
/* @} */
|
/* @} */
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
BIN
virtualsmartcard/npa-example-data/ZZSTCVCA00001.cvcert
Normal file
BIN
virtualsmartcard/npa-example-data/ZZSTCVCA00001.cvcert
Normal file
Binary file not shown.
8
virtualsmartcard/npa-example-data/ZZSTCVCA00001.pem
Normal file
8
virtualsmartcard/npa-example-data/ZZSTCVCA00001.pem
Normal 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-----
|
||||||
BIN
virtualsmartcard/npa-example-data/ZZSTCVCA00001.pkcs8
Normal file
BIN
virtualsmartcard/npa-example-data/ZZSTCVCA00001.pkcs8
Normal file
Binary file not shown.
BIN
virtualsmartcard/npa-example-data/ZZSTDVCA00001.cvcert
Normal file
BIN
virtualsmartcard/npa-example-data/ZZSTDVCA00001.cvcert
Normal file
Binary file not shown.
BIN
virtualsmartcard/npa-example-data/ZZSTDVCA00001.pkcs8
Normal file
BIN
virtualsmartcard/npa-example-data/ZZSTDVCA00001.pkcs8
Normal file
Binary file not shown.
BIN
virtualsmartcard/npa-example-data/ZZSTTERM00001.cvcert
Normal file
BIN
virtualsmartcard/npa-example-data/ZZSTTERM00001.cvcert
Normal file
Binary file not shown.
BIN
virtualsmartcard/npa-example-data/ZZSTTERM00001.pkcs8
Normal file
BIN
virtualsmartcard/npa-example-data/ZZSTTERM00001.pkcs8
Normal file
Binary file not shown.
@@ -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()
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|
||||||
|
|||||||
@@ -397,8 +397,6 @@ class Security_Environment(object):
|
|||||||
p1 = CAPDU.p1
|
p1 = CAPDU.p1
|
||||||
if p2 == None:
|
if p2 == None:
|
||||||
p2 = CAPDU.p2
|
p2 = CAPDU.p2
|
||||||
if le == None:
|
|
||||||
le = CAPDU.le
|
|
||||||
# FIXME
|
# FIXME
|
||||||
#if expected != "":
|
#if expected != "":
|
||||||
#raise SwError(SW["ERR_SECMESSOBJECTSMISSING"])
|
#raise SwError(SW["ERR_SECMESSOBJECTSMISSING"])
|
||||||
|
|||||||
@@ -242,7 +242,10 @@ class Iso7816OS(SmartcardOS):
|
|||||||
if le > len(data):
|
if le > len(data):
|
||||||
sw = SW["WARN_EOFBEFORENEREAD"]
|
sw = SW["WARN_EOFBEFORENEREAD"]
|
||||||
|
|
||||||
result = data[:le]
|
if le != None:
|
||||||
|
result = data[:le]
|
||||||
|
else:
|
||||||
|
result = data[:0]
|
||||||
if sm:
|
if sm:
|
||||||
sw, result = self.SAM.protect_result(sw, result)
|
sw, result = self.SAM.protect_result(sw, result)
|
||||||
|
|
||||||
@@ -500,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
|
||||||
@@ -523,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):
|
||||||
@@ -534,7 +544,10 @@ class NPAOS(Iso7816OS):
|
|||||||
if le > len(data) and le != MAX_EXTENDED_LE and le != MAX_SHORT_LE:
|
if le > len(data) and le != MAX_EXTENDED_LE and le != MAX_SHORT_LE:
|
||||||
sw = SW["WARN_EOFBEFORENEREAD"]
|
sw = SW["WARN_EOFBEFORENEREAD"]
|
||||||
|
|
||||||
result = data[:le]
|
if le != None:
|
||||||
|
result = data[:le]
|
||||||
|
else:
|
||||||
|
result = data[:0]
|
||||||
if sm:
|
if sm:
|
||||||
try:
|
try:
|
||||||
sw, result = self.SAM.protect_result(sw, result)
|
sw, result = self.SAM.protect_result(sw, result)
|
||||||
@@ -636,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,
|
||||||
@@ -668,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":
|
||||||
|
|||||||
@@ -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"):
|
||||||
|
|||||||
Reference in New Issue
Block a user