removed npa project

It has been replaced by https://github.com/frankmorgner/OpenSC
This commit is contained in:
Frank Morgner
2016-10-05 20:51:13 +02:00
parent c72f0a1077
commit a8b22f7bef
72 changed files with 469 additions and 11349 deletions

7
.gitmodules vendored
View File

@@ -1,7 +1,3 @@
[submodule "npa/src/opensc"]
path = npa/src/opensc
url = git://github.com/OpenSC/OpenSC.git
ignore = dirty
[submodule "ACardEmulator/app/src/main/external/ykneo-openpgp"] [submodule "ACardEmulator/app/src/main/external/ykneo-openpgp"]
path = ACardEmulator/app/src/main/external/ykneo-openpgp path = ACardEmulator/app/src/main/external/ykneo-openpgp
url = https://github.com/Yubico/ykneo-openpgp.git url = https://github.com/Yubico/ykneo-openpgp.git
@@ -17,3 +13,6 @@
[submodule "ACardEmulator/app/src/main/external/GidsApplet"] [submodule "ACardEmulator/app/src/main/external/GidsApplet"]
path = ACardEmulator/app/src/main/external/GidsApplet path = ACardEmulator/app/src/main/external/GidsApplet
url = https://github.com/vletoux/GidsApplet.git url = https://github.com/vletoux/GidsApplet.git
[submodule "ccid/src/OpenSC"]
path = ccid/src/OpenSC
url = https://github.com/frankmorgner/OpenSC

View File

@@ -39,25 +39,8 @@ install:
fi fi
before_script: before_script:
# libnpa requires OpenPACE # ccid requires OpenSC
- git clone https://github.com/frankmorgner/openpace && cd openpace - git clone https://github.com/frankmorgner/OpenSC && cd OpenSC
- autoreconf -vis
- if [ -z "$HOST" ]; then
./configure --prefix=$PREFIX --enable-openssl-install --enable-python || cat config.log;
else
export CROSS_COMPILE=$HOST-;
./configure --prefix=$PREFIX --enable-openssl-install --host=$HOST --enable-openssl-install || cat config.log;
touch src/cvc-create.1 src/cvc-print.1;
fi
- make install
# libnpa requires OpenSC
- cd $TRAVIS_BUILD_DIR/npa/src/opensc
- grep _sc_card_add_ec_alg src/libopensc/libopensc.exports || echo _sc_card_add_ec_alg >> src/libopensc/libopensc.exports
- grep _sc_card_add_rsa_alg src/libopensc/libopensc.exports || echo _sc_card_add_rsa_alg >> src/libopensc/libopensc.exports
- grep _sc_match_atr src/libopensc/libopensc.exports || echo _sc_match_atr >> src/libopensc/libopensc.exports
- grep sc_apdu_set_resp src/libopensc/libopensc.exports || echo sc_apdu_set_resp >> src/libopensc/libopensc.exports
- grep sc_apdu_get_octets src/libopensc/libopensc.exports || echo sc_apdu_get_octets >> src/libopensc/libopensc.exports
- grep sc_pkcs15_pincache_add src/libopensc/libopensc.exports || echo sc_pkcs15_pincache_add >> src/libopensc/libopensc.exports
- ./bootstrap - ./bootstrap
- if [ -z "$HOST" ]; then - if [ -z "$HOST" ]; then
./configure --prefix=$PREFIX || cat config.log; ./configure --prefix=$PREFIX || cat config.log;
@@ -65,15 +48,6 @@ before_script:
./configure --prefix=$PREFIX --host=$HOST LDFLAGS="-I$PREFIX/include" CFLAGS="-I$PREFIX/include" CXXFLAGS="-I$PREFIX/include" LIBS="-I$PREFIX/include" || cat config.log; ./configure --prefix=$PREFIX --host=$HOST LDFLAGS="-I$PREFIX/include" CFLAGS="-I$PREFIX/include" CXXFLAGS="-I$PREFIX/include" LIBS="-I$PREFIX/include" || cat config.log;
fi fi
- make install - make install
# Configure libnpa
- cd $TRAVIS_BUILD_DIR/npa
- autoreconf -vsi
- if [ -z "$HOST" ]; then
./configure OPENSC_LIBS="-L$PREFIX/lib -lopensc -lcrypto" || cat config.log;
else
./configure --host=$HOST OPENSC_LIBS="-L$PREFIX/lib -lopensc -lcrypto" || cat config.log;
touch src/npa-tool.1;
fi
# Configure virtualsmartcard # Configure virtualsmartcard
- cd $TRAVIS_BUILD_DIR/virtualsmartcard && autoreconf -vsi - cd $TRAVIS_BUILD_DIR/virtualsmartcard && autoreconf -vsi
- if [ -z "$HOST" ]; then - if [ -z "$HOST" ]; then
@@ -94,7 +68,7 @@ before_script:
./configure --host=$HOST || cat config.log; ./configure --host=$HOST || cat config.log;
fi fi
- if [ -z "$HOST" ]; then - if [ -z "$HOST" ]; then
cd $TRAVIS_BUILD_DIR/ccid && autoreconf -vsi && ./configure OPENSSL_CFLAGS="-I$PREFIX/include" OPENSSL_LIBS="-L$PREFIX/lib -lcrypto" OPENSC_LIBS="-L$PREFIX/lib -lopensc" || cat config.log; cd $TRAVIS_BUILD_DIR/ccid && autoreconf -vsi && ./configure OPENSC_LIBS="-L$PREFIX/lib -lopensc" || cat config.log;
fi fi
addons: addons:
@@ -107,8 +81,6 @@ addons:
branch_pattern: coverity_scan branch_pattern: coverity_scan
script: script:
# Build libnpa
- make -C $TRAVIS_BUILD_DIR/npa
# Build virtualsmartcard # Build virtualsmartcard
- make -C $TRAVIS_BUILD_DIR/virtualsmartcard - make -C $TRAVIS_BUILD_DIR/virtualsmartcard
# Verify virtualsmartcard # Verify virtualsmartcard

View File

@@ -75,28 +75,8 @@ CPPFLAGS="$saved_CPPFLAGS"
LIBS="$saved_LIBS" LIBS="$saved_LIBS"
# --enable-pace # --enable-pace
WITH_PACE=no
AC_ARG_ENABLE(pace,
AS_HELP_STRING([--enable-pace], [Enable Password Authenticated Connection Establishment (PACE) @<:@detect@:>@]),
[enable_pace="${enableval}"], [enable_pace=detect])
saved_CPPFLAGS="$CPPFLAGS" saved_CPPFLAGS="$CPPFLAGS"
saved_LIBS="$LIBS" saved_LIBS="$LIBS"
if test "x$enable_pace" != xno ; then
WITH_PACE=yes
PKG_CHECK_EXISTS([libnpa],
[PKG_CHECK_MODULES([LIBNPA], [libnpa])],
[AC_MSG_WARN([libnpa not found by pkg-config])])
CPPFLAGS="$CPPFLAGS $LIBNPA_CFLAGS -I${srcdir}/src/opensc/src"
LIBS="$LDFLAGS $LIBNPA_LIBS"
AC_CHECK_HEADERS(npa/npa.h, [], [ WITH_PACE=no ])
AC_MSG_CHECKING([for perform_pace])
AC_TRY_LINK_FUNC(perform_pace, [ AC_MSG_RESULT([yes]) ], [ WITH_PACE=no ])
if test x$WITH_PACE = xno -a x$enable_pace = xyes ; then
AC_MSG_ERROR([libnpa not found, use ./configure LIBNPA_CFLAGS=... LIBNPA_LIBS=...])
fi
fi
if test "x$WITH_PACE" == xno ; then
PKG_CHECK_EXISTS([libopensc], PKG_CHECK_EXISTS([libopensc],
[PKG_CHECK_MODULES([OPENSC], [libopensc >= 0.12.1])], [PKG_CHECK_MODULES([OPENSC], [libopensc >= 0.12.1])],
[AC_MSG_WARN([libopensc >= 0.12.1 not found by pkg-config])]) [AC_MSG_WARN([libopensc >= 0.12.1 not found by pkg-config])])
@@ -105,14 +85,10 @@ if test "x$WITH_PACE" == xno ; then
AC_MSG_CHECKING([for sc_bytes2apdu]) AC_MSG_CHECKING([for sc_bytes2apdu])
AC_TRY_LINK_FUNC(sc_bytes2apdu, [ AC_MSG_RESULT([yes]) ], AC_TRY_LINK_FUNC(sc_bytes2apdu, [ AC_MSG_RESULT([yes]) ],
[ AC_MSG_ERROR([libopensc >= 0.12.1 not found, use ./configure OPENSC_LIBS=...]) ]) [ AC_MSG_ERROR([libopensc >= 0.12.1 not found, use ./configure OPENSC_LIBS=...]) ])
fi AC_CHECK_FUNCS([sc_apdu_set_resp sc_apdu_get_octets _sc_match_atr boxing_buf_to_pace_input])
AC_CHECK_FUNCS([sc_apdu_set_resp sc_apdu_get_octets _sc_match_atr])
CPPFLAGS="$saved_CPPFLAGS" CPPFLAGS="$saved_CPPFLAGS"
LIBS="$saved_LIBS" LIBS="$saved_LIBS"
AM_CONDITIONAL(WITH_PACE, test "${WITH_PACE}" = "yes")
AM_COND_IF(WITH_PACE, [AC_DEFINE(WITH_PACE, 1, [enable PACE support])])
PACKAGE_SUMMARY="Emulate a USB CCID compliant smart card reader" PACKAGE_SUMMARY="Emulate a USB CCID compliant smart card reader"
AC_SUBST(PACKAGE_SUMMARY) AC_SUBST(PACKAGE_SUMMARY)
@@ -157,10 +133,6 @@ PTHREAD_LIBS: ${PTHREAD_LIBS}
OPENSC_LIBS: ${OPENSC_LIBS} OPENSC_LIBS: ${OPENSC_LIBS}
OPENSSL_CFLAGS: ${OPENSSL_CFLAGS} OPENSSL_CFLAGS: ${OPENSSL_CFLAGS}
OPENSSL_LIBS: ${OPENSSL_LIBS} OPENSSL_LIBS: ${OPENSSL_LIBS}
LIBNPA_CFLAGS: ${LIBNPA_CFLAGS}
LIBNPA_LIBS: ${LIBNPA_LIBS}
PACE support: ${WITH_PACE}
HELP2MAN: ${HELP2MAN} HELP2MAN: ${HELP2MAN}

View File

@@ -10,14 +10,14 @@ do_subst = $(SED) \
BUILT_SOURCES = cmdline.h cmdline.c BUILT_SOURCES = cmdline.h cmdline.c
EXTRA_DIST = ccid-emulator.ggo ccid-emulator.ggo.in EXTRA_DIST = ccid-emulator.ggo ccid-emulator.ggo.in
EXTRA_DIST += $(shell find $(top_srcdir)/src/opensc/src -path '*/.git' -prune -o -type f -a -name '*.h' -print) EXTRA_DIST += $(shell find $(top_srcdir)/src/OpenSC/src -path '*/.git' -prune -o -type f -a -name '*.h' -print)
MAINTAINERCLEANFILES = $(BUILT_SOURCES) ccid-emulator.ggo $(dist_man1_MANS) MAINTAINERCLEANFILES = $(BUILT_SOURCES) ccid-emulator.ggo $(dist_man1_MANS)
dist_man1_MANS = $(abs_builddir)/ccid-emulator.1 dist_man1_MANS = $(abs_builddir)/ccid-emulator.1
ccid_emulator_SOURCES = ccid.c usbstring.c usb.c $(BUILT_SOURCES) ccid_emulator_SOURCES = ccid.c usbstring.c usb.c scutil.c $(BUILT_SOURCES)
ccid_emulator_LDADD = $(OPENSSL_LIBS) $(PTHREAD_LIBS) ccid_emulator_LDADD = $(OPENSC_LIBS) $(OPENSSL_LIBS) $(PTHREAD_LIBS)
ccid_emulator_CFLAGS = $(OPENSSL_CFLAGS) $(PTHREAD_CFLAGS) -I$(top_srcdir)/src/opensc/src ccid_emulator_CFLAGS = $(OPENSSL_CFLAGS) $(PTHREAD_CFLAGS) -I$(top_srcdir)/src/OpenSC/src
ccid-emulator.c: $(BUILT_SOURCES) ccid-emulator.c: $(BUILT_SOURCES)
@@ -43,15 +43,5 @@ bin_PROGRAMS = ccid-emulator
noinst_HEADERS = \ noinst_HEADERS = \
ccid.h \ ccid.h \
ccid-types.h \ ccid-types.h \
npa/scutil.h \ scutil.h \
sslutil.h \
usbstring.h usbstring.h
if WITH_PACE
ccid_emulator_LDADD += $(LIBNPA_LIBS)
ccid_emulator_CFLAGS += $(LIBNPA_CFLAGS)
else
ccid_emulator_SOURCES += scutil.c
ccid_emulator_LDADD += $(OPENSC_LIBS)
endif

1
ccid/src/OpenSC Submodule

Submodule ccid/src/OpenSC added at 78882fd927

View File

@@ -19,6 +19,8 @@
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <libopensc/log.h> #include <libopensc/log.h>
#include <libopensc/opensc.h> #include <libopensc/opensc.h>
#include <libopensc/reader-boxing.h>
#include <sm/sm-eac.h>
#include <openssl/evp.h> #include <openssl/evp.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #include <stdint.h>
@@ -28,20 +30,18 @@
#include <unistd.h> #include <unistd.h>
#include "ccid.h" #include "ccid.h"
#include "sslutil.h"
#include "config.h" #include "config.h"
#include <npa/scutil.h> #include <npa/scutil.h>
#ifndef HAVE_BOXING_BUF_TO_PACE_INPUT
#include <libopensc/reader-boxing.c>
#endif
static sc_context_t *ctx = NULL; static sc_context_t *ctx = NULL;
static sc_card_t *card = NULL; static sc_card_t *card = NULL;
static sc_reader_t *reader = NULL; static sc_reader_t *reader = NULL;
#ifdef WITH_PACE
#include <npa/boxing.h>
#include <npa/iso-sm.h>
#include <npa/npa.h>
static int static int
perform_pseudo_apdu_EstablishPACEChannel(sc_apdu_t *apdu) perform_pseudo_apdu_EstablishPACEChannel(sc_apdu_t *apdu)
{ {
@@ -88,9 +88,6 @@ perform_pseudo_apdu_GetReaderPACECapabilities(sc_apdu_t *apdu)
return boxing_pace_capabilities_to_buf(reader->ctx, return boxing_pace_capabilities_to_buf(reader->ctx,
sc_reader_t_capabilities, &apdu->resp, &apdu->resplen); sc_reader_t_capabilities, &apdu->resp, &apdu->resplen);
} }
#else
int sc_sm_stop(struct sc_card *card) { return SC_SUCCESS; }
#endif
static int static int
perform_PC_to_RDR_GetSlotStatus(const __u8 *in, size_t inlen, __u8 **out, size_t *outlen); perform_PC_to_RDR_GetSlotStatus(const __u8 *in, size_t inlen, __u8 **out, size_t *outlen);
@@ -577,29 +574,19 @@ perform_pseudo_apdu(sc_reader_t *reader, sc_apdu_t *apdu)
break; break;
case 0x01: case 0x01:
/* GetReaderPACECapabilities */ /* GetReaderPACECapabilities */
#ifdef WITH_PACE
LOG_TEST_RET(ctx, LOG_TEST_RET(ctx,
perform_pseudo_apdu_GetReaderPACECapabilities(apdu), perform_pseudo_apdu_GetReaderPACECapabilities(apdu),
"Could not get reader's PACE Capabilities"); "Could not get reader's PACE Capabilities");
apdu->sw1 = iso_sw_ok.sw1; apdu->sw1 = iso_sw_ok.sw1;
apdu->sw2 = iso_sw_ok.sw2; apdu->sw2 = iso_sw_ok.sw2;
#else
apdu->sw1 = 0x6D;
apdu->sw2 = 0x00;
#endif
break; break;
case 0x02: case 0x02:
/* EstablishPACEChannel */ /* EstablishPACEChannel */
#ifdef WITH_PACE
LOG_TEST_RET(ctx, LOG_TEST_RET(ctx,
perform_pseudo_apdu_EstablishPACEChannel(apdu), perform_pseudo_apdu_EstablishPACEChannel(apdu),
"Could not perform PACE"); "Could not perform PACE");
apdu->sw1 = iso_sw_ok.sw1; apdu->sw1 = iso_sw_ok.sw1;
apdu->sw2 = iso_sw_ok.sw2; apdu->sw2 = iso_sw_ok.sw2;
#else
apdu->sw1 = 0x6D;
apdu->sw2 = 0x00;
#endif
break; break;
case 0x03: case 0x03:
/* DestroyPACEChannel */ /* DestroyPACEChannel */
@@ -949,7 +936,6 @@ write_pin(sc_apdu_t *apdu, struct sc_pin_cmd_pin *pin, uint8_t blocksize,
blocksize - justify_offset, pin, encoding, sc_result); blocksize - justify_offset, pin, encoding, sc_result);
} }
#ifdef WITH_PACE
static int static int
perform_PC_to_RDR_Secure_EstablishPACEChannel(sc_card_t *card, perform_PC_to_RDR_Secure_EstablishPACEChannel(sc_card_t *card,
const __u8 *abData, size_t abDatalen, const __u8 *abData, size_t abDatalen,
@@ -1197,23 +1183,6 @@ perform_PC_to_RDR_Secure_GetReadersPACECapabilities(__u8 **abDataOut,
return SC_SUCCESS; return SC_SUCCESS;
} }
#else
static int
perform_PC_to_RDR_Secure_EstablishPACEChannel(sc_card_t *card,
const __u8 *abData, size_t abDatalen,
__u8 **abDataOut, size_t *abDataOutLen)
{
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "ccid compiled without PACE support.");
return SC_ERROR_NOT_SUPPORTED;
}
static int
perform_PC_to_RDR_Secure_GetReadersPACECapabilities(__u8 **abDataOut,
size_t *abDataOutLen)
{
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "ccid compiled without PACE support.");
return SC_ERROR_NOT_SUPPORTED;
}
#endif
static int static int
perform_PC_to_RDR_Secure(const __u8 *in, size_t inlen, __u8** out, size_t *outlen) perform_PC_to_RDR_Secure(const __u8 *in, size_t inlen, __u8** out, size_t *outlen)
@@ -1394,7 +1363,6 @@ perform_PC_to_RDR_Secure(const __u8 *in, size_t inlen, __u8** out, size_t *outle
0)) { 0)) {
sc_result = SC_ERROR_INTERNAL; sc_result = SC_ERROR_INTERNAL;
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Could not read PIN.\n"); sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Could not read PIN.\n");
ssl_error(ctx);
goto err; goto err;
} }
} else { } else {
@@ -1406,7 +1374,6 @@ perform_PC_to_RDR_Secure(const __u8 *in, size_t inlen, __u8** out, size_t *outle
0)) { 0)) {
sc_result = SC_ERROR_INTERNAL; sc_result = SC_ERROR_INTERNAL;
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Could not read current PIN.\n"); sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Could not read current PIN.\n");
ssl_error(ctx);
goto err; goto err;
} }
} }
@@ -1417,7 +1384,6 @@ perform_PC_to_RDR_Secure(const __u8 *in, size_t inlen, __u8** out, size_t *outle
modify->bConfirmPIN & CCID_PIN_CONFIRM_NEW)) { modify->bConfirmPIN & CCID_PIN_CONFIRM_NEW)) {
sc_result = SC_ERROR_INTERNAL; sc_result = SC_ERROR_INTERNAL;
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Could not read new PIN.\n"); sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "Could not read new PIN.\n");
ssl_error(ctx);
goto err; goto err;
} }
} }

View File

@@ -1 +0,0 @@
../../npa/src/npa

View File

@@ -1 +0,0 @@
../../npa/src/opensc/

View File

@@ -1 +0,0 @@
../../npa/src/scutil.c

345
ccid/src/scutil.c Normal file
View File

@@ -0,0 +1,345 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of libnpa.
*
* libnpa is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libnpa 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* libnpa. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "libopensc/internal.h"
#ifndef HAVE__SC_MATCH_ATR
int sc_dlclose(void *handle) {}
const char *sc_dlerror() {}
void *sc_dlsym(void *handle, const char *symbol) {}
size_t strlcpy(char *dst, const char *src, size_t siz) {}
int sc_mutex_create(const sc_context_t *ctx, void **mutex) {}
int sc_mutex_destroy(const sc_context_t *ctx, void *mutex) {}
int _sc_parse_atr(sc_reader_t *reader) {}
void *sc_dlopen(const char *filename) {}
int sc_mutex_lock(const sc_context_t *ctx, void *mutex) {}
int sc_mutex_unlock(const sc_context_t *ctx, void *mutex) {}
#include "libopensc/card.c"
#endif
#if !defined(HAVE_SC_APDU_GET_OCTETS) || !defined(HAVE_SC_APDU_SET_RESP)
#ifdef HAVE__SC_MATCH_ATR
size_t sc_get_max_send_size(const sc_card_t *card) {}
#endif
#include "libopensc/apdu.c"
#endif
#include <libopensc/log.h>
#include <npa/iso-sm.h>
#include <npa/scutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int initialize(int reader_id, int verbose,
sc_context_t **ctx, sc_reader_t **reader)
{
unsigned int i, reader_count;
if (!ctx || !reader)
return SC_ERROR_INVALID_ARGUMENTS;
int r = sc_establish_context(ctx, "");
if (r < 0 || !*ctx) {
fprintf(stderr, "Failed to create initial context: %s", sc_strerror(r));
return r;
}
(*ctx)->debug = verbose;
(*ctx)->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER;
reader_count = sc_ctx_get_reader_count(*ctx);
if (reader_count == 0) {
sc_debug(*ctx, SC_LOG_DEBUG_NORMAL, "No reader not found.\n");
return SC_ERROR_NO_READERS_FOUND;
}
if (reader_id < 0) {
/* Automatically try to skip to a reader with a card if reader not specified */
for (i = 0; i < reader_count; i++) {
*reader = sc_ctx_get_reader(*ctx, i);
if (sc_detect_card_presence(*reader) & SC_READER_CARD_PRESENT) {
reader_id = i;
sc_debug(*ctx, SC_LOG_DEBUG_NORMAL, "Using the first reader"
" with a card: %s", (*reader)->name);
break;
}
}
if (reader_id >= reader_count) {
sc_debug(*ctx, SC_LOG_DEBUG_NORMAL, "No card found, using the first reader.");
reader_id = 0;
}
}
if (reader_id >= reader_count) {
sc_debug(*ctx, SC_LOG_DEBUG_NORMAL, "Invalid reader number "
"(%d), only %d available.\n", reader_id, reader_count);
return SC_ERROR_NO_READERS_FOUND;
}
*reader = sc_ctx_get_reader(*ctx, reader_id);
return SC_SUCCESS;
}
void _bin_log(sc_context_t *ctx, int type, const char *file, int line,
const char *func, const char *label, const u8 *data, size_t len,
FILE *f)
{
if (!f) {
char buf[1800];
if (data)
sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, data, len, buf, sizeof buf);
else
buf[0] = 0;
sc_do_log(ctx, type, file, line, func,
"\n%s (%u byte%s)%s%s",
label, (unsigned int) len, len==1?"":"s", len==0?"":":\n", buf);
} else {
fprintf(f, "%s (%u byte%s)%s%s\n",
label, (unsigned int) len, len==1?"":"s", len==0?"":":\n", sc_dump_hex(data, len));
}
}
static int list_readers(sc_context_t *ctx)
{
char card_atr[0x3e];
sc_card_t *card;
sc_reader_t *reader;
size_t i, rcount = sc_ctx_get_reader_count(ctx);
if (rcount == 0) {
printf("No smart card readers found.\n");
return 0;
}
printf("%-4s %-7s %s\n", "Nr.", "Driver", "Smart Card Reader");
for (i = 0; i < rcount; i++) {
reader = sc_ctx_get_reader(ctx, i);
memset(card_atr, '\0', sizeof card_atr);
if (sc_detect_card_presence(reader) & SC_READER_CARD_PRESENT) {
if (sc_connect_card(reader, &card) == SC_SUCCESS) {
sc_bin_to_hex(card->atr.value, card->atr.len,
card_atr, sizeof card_atr, ':');
}
sc_disconnect_card(card);
} else {
strncpy(card_atr, "[no card present]", sizeof card_atr);
}
printf("%-4zu %-7s %s\n", i, reader->driver->short_name, reader->name);
printf(" ATR: %s\n", card_atr);
}
return 0;
}
int print_avail(int verbose)
{
sc_context_t *ctx = NULL;
int r;
r = sc_establish_context(&ctx, "");
if (r) {
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
return 1;
}
ctx->debug = verbose;
ctx->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER;
r = list_readers(ctx);
sc_release_context(ctx);
return r;
}
#define ISO_READ_BINARY 0xB0
#define ISO_P1_FLAG_SFID 0x80
int read_binary_rec(sc_card_t *card, unsigned char sfid,
u8 **ef, size_t *ef_len)
{
int r;
size_t read = MAX_SM_APDU_RESP_SIZE;
sc_apdu_t apdu;
u8 *p;
if (!card || !ef || !ef_len) {
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
*ef_len = 0;
if (read > 0xff+1)
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_EXT,
ISO_READ_BINARY, ISO_P1_FLAG_SFID|sfid, 0);
else
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT,
ISO_READ_BINARY, ISO_P1_FLAG_SFID|sfid, 0);
p = realloc(*ef, read);
if (!p) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
*ef = p;
apdu.resp = *ef;
apdu.resplen = read;
apdu.le = read;
r = sc_transmit_apdu(card, &apdu);
/* emulate the behaviour of sc_read_binary */
if (r >= 0)
r = apdu.resplen;
while(1) {
if (r >= 0 && r != read) {
*ef_len += r;
break;
}
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not read EF.");
goto err;
}
*ef_len += r;
p = realloc(*ef, *ef_len + read);
if (!p) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
*ef = p;
r = sc_read_binary(card, *ef_len,
*ef + *ef_len, read, 0);
}
r = SC_SUCCESS;
err:
return r;
}
#define ISO_WRITE_BINARY 0xD0
int write_binary_rec(sc_card_t *card, unsigned char sfid,
u8 *ef, size_t ef_len)
{
int r;
size_t write = MAX_SM_APDU_DATA_SIZE, wrote = 0;
sc_apdu_t apdu;
#ifdef ENABLE_SM
struct iso_sm_ctx *iso_sm_ctx;
#endif
if (!card) {
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
#ifdef ENABLE_SM
iso_sm_ctx = card->sm_ctx.info.cmd_data;
if (write > SC_MAX_APDU_BUFFER_SIZE-2
|| (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT
&& write > (((SC_MAX_APDU_BUFFER_SIZE-2
/* for encrypted APDUs we usually get authenticated status
* bytes (4B), a MAC (11B) and a cryptogram with padding
* indicator (3B without data). The cryptogram is always
* padded to the block size. */
-18) / iso_sm_ctx->block_length)
* iso_sm_ctx->block_length - 1)))
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_EXT,
ISO_WRITE_BINARY, ISO_P1_FLAG_SFID|sfid, 0);
else
#endif
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT,
ISO_WRITE_BINARY, ISO_P1_FLAG_SFID|sfid, 0);
if (write > ef_len) {
apdu.datalen = ef_len;
apdu.lc = ef_len;
} else {
apdu.datalen = write;
apdu.lc = write;
}
apdu.data = ef;
r = sc_transmit_apdu(card, &apdu);
/* emulate the behaviour of sc_write_binary */
if (r >= 0)
r = apdu.datalen;
while (1) {
if (r < 0 || r > ef_len) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not write EF.");
goto err;
}
wrote += r;
apdu.data += r;
if (wrote >= ef_len)
break;
r = sc_write_binary(card, wrote, ef, write, 0);
}
r = SC_SUCCESS;
err:
return r;
}
int fread_to_eof(const char *file, unsigned char **buf, size_t *buflen)
{
FILE *input = NULL;
int r = 0;
unsigned char *p;
if (!buflen || !buf || !file)
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;
}

View File

@@ -1 +0,0 @@
../../npa/src/sslutil.h

View File

@@ -11,16 +11,13 @@
# All configuration values have a default; values that are commented out # All configuration values have a default; values that are commented out
# serve to show the default. # serve to show the default.
import sys, os, breathe, sphinx_bootstrap_theme import sys, os, sphinx_bootstrap_theme
from sphinxcontrib.doxylink import doxylink
from datetime import date from datetime import date
os.system("make doc -C npa")
os.system("make doc -C virtualsmartcard") os.system("make doc -C virtualsmartcard")
os.system("make doc -C ccid") os.system("make doc -C ccid")
os.system("make doc -C pcsc-relay") os.system("make doc -C pcsc-relay")
os.environ['PATH'] = os.path.abspath('../npa/src') + os.pathsep + os.environ['PATH']
os.environ['PATH'] = os.path.abspath('../pcsc-relay/src') + os.pathsep + os.environ['PATH'] os.environ['PATH'] = os.path.abspath('../pcsc-relay/src') + os.pathsep + os.environ['PATH']
os.environ['PATH'] = os.path.abspath('../virtualsmartcard/src/vpicc') + os.pathsep + os.environ['PATH'] os.environ['PATH'] = os.path.abspath('../virtualsmartcard/src/vpicc') + os.pathsep + os.environ['PATH']
@@ -36,7 +33,7 @@ needs_sphinx = '1.1'
# Add any Sphinx extension module names here, as strings. They can be extensions # Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ["breathe", "sphinxcontrib.doxylink", "sphinx.ext.autosummary", "sphinxcontrib.programoutput", "sphinx.ext.inheritance_diagram", "sphinxcontrib.tikz", 'sphinxcontrib.bibtex'] extensions = ["sphinx.ext.autosummary", "sphinxcontrib.programoutput", "sphinx.ext.inheritance_diagram", "sphinxcontrib.tikz", 'sphinxcontrib.bibtex']
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates'] templates_path = ['_templates']
@@ -247,8 +244,3 @@ man_pages = [
('index', 'virtualsmartcardarchitecture', u'Virtual Smartcard Architecture Documentation', ('index', 'virtualsmartcardarchitecture', u'Virtual Smartcard Architecture Documentation',
[u'Dominik Oepen, Frank Morgner'], 1) [u'Dominik Oepen, Frank Morgner'], 1)
] ]
breathe_projects = {"npa": "npa/xml"}
breathe_default_project = "npa"
doxylink = { 'npa' : ('npa/npa.tag', '_static/doxygen-npa/'), }

View File

@@ -50,7 +50,6 @@ Currently the following projects are part of |vsmartcard|:
ACardEmulator/README ACardEmulator/README
pcsc-relay/README pcsc-relay/README
ccid/README ccid/README
npa/README
Looking for a feature the included programs do not offer? Want to extend the Looking for a feature the included programs do not offer? Want to extend the
functionality of some library? Apart from requesting a new feature, you can functionality of some library? Apart from requesting a new feature, you can
@@ -60,7 +59,6 @@ have a look at these programming guides and try yourself:
:maxdepth: 1 :maxdepth: 1
virtualsmartcard/api virtualsmartcard/api
npa/api
.. image:: https://img.shields.io/travis/frankmorgner/vsmartcard/master.svg?label=Travis%20CI%20build .. image:: https://img.shields.io/travis/frankmorgner/vsmartcard/master.svg?label=Travis%20CI%20build
:target: https://travis-ci.org/frankmorgner/vsmartcard :target: https://travis-ci.org/frankmorgner/vsmartcard

View File

@@ -1 +0,0 @@
../npa/doc

View File

@@ -1 +0,0 @@
Frank Morgner <frankmorgner@gmail.com>

View File

@@ -1,674 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

File diff suppressed because it is too large Load Diff

View File

@@ -1,48 +0,0 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src m4 doc cross certs
EXTRA_DIST = libnpa.pc.in opensc.conf.in apdus test_vicc_with_npa-tool.sh
do_subst = $(SED) \
-e 's,[@]PACKAGE_NAME[@],$(PACKAGE_NAME),g' \
-e 's,[@]PACKAGE_VERSION[@],$(PACKAGE_VERSION),g' \
-e 's,[@]PACKAGE_SUMMARY[@],$(PACKAGE_SUMMARY),g' \
-e 's,[@]builddir[@],$(builddir),g' \
-e 's,[@]prefix[@],$(prefix),g' \
-e 's,[@]exec_prefix[@],$(exec_prefix),g' \
-e 's,[@]libdir[@],$(libdir),g' \
-e 's,[@]DYN_LIB_EXT[@],$(DYN_LIB_EXT),g' \
-e 's,[@]includedir[@],$(includedir),g' \
-e 's,[@]VERSION[@],$(VERSION),g' \
-e 's,[@]OPENSC_LIBS[@],$(OPENSC_LIBS),g' \
-e 's,[@]top_srcdir[@],$(top_srcdir),g'
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libnpa.pc
libnpa.pc: libnpa.pc.in
$(do_subst) < $(srcdir)/libnpa.pc.in > libnpa.pc
opensc.conf: opensc.conf.in
$(do_subst) < $(srcdir)/opensc.conf.in > opensc.conf
win:
make -C cross $@
android:
make -C cross $@
osx:
make -C cross $@
all-local: opensc.conf
clean-local:
rm -f libnpa.pc opensc.conf
distcheck-hook:
test -d .svn && \
svn2cl --group-by-day --reparagraph --separate-daylogs --include-actions --include-rev \
|| true
test -d ../.git && \
git log --pretty --numstat --summary . | git2cl > ChangeLog \
|| true

View File

View File

@@ -1 +0,0 @@
doc/README.txt

7
npa/README Normal file
View File

@@ -0,0 +1,7 @@
#nPA Smart Card Library
Access the German electronic identity card (neuer Personalausweis/nPA).
The functionality of nPA Smart Card Library has been integrated into
OpenSC. Developement and Support will continue at our new home
https://github.com/frankmorgner/OpenSC.

View File

@@ -1,29 +0,0 @@
00:22:81:B6:0F:83:0D:5A:5A:43:56:43:41:41:54:41:30:30:30:31
00:2a:00:be:e4:7f:4e:81:9d:5f:29:01:00:42:0d:5a:5a:43:56:43:41:41:54:41:30:30:30:31:7f:49:4f:06:0a:04:00:7f:00:07:02:02:02:02:03:86:41:04:52:dd:32:ea:fe:1f:bb:b4:00:0c:d9:ce:75:f6:66:36:cf:cf:1e:dd:44:f7:b1:ed:ae:25:b8:41:93:da:04:a9:1c:77:ee:87:f5:c8:f9:59:ed:27:62:00:de:33:ab:57:4c:e9:80:11:35:ff:44:97:a3:71:62:b7:c8:54:8a:0c:5f:20:0e:5a:5a:44:56:43:41:41:54:41:30:30:30:30:35:7f:4c:12:06:09:04:00:7f:00:07:03:01:02:02:53:05:70:03:01:ff:b7:5f:25:06:01:00:00:06:01:01:5f:24:06:01:00:01:00:03:01:5f:37:40:6f:13:ae:9a:6f:4e:dd:b7:83:9f:f3:f0:4d:71:e0:dc:37:7b:c4:b0:8f:ad:29:5e:ed:24:1b:52:43:28:ad:07:30:eb:55:34:97:b4:fb:66:e9:bb:7a:b9:08:15:f0:42:73:f0:9e:75:1d:7f:d4:b8:61:43:9b:4e:e6:53:81:c3
002281B610830E5A5A445643414154413030303035
002a00be0001417f4e81fa5f290100420e5a5a4456434141544130303030357f494f060a04007f000702020202038641049bfe7415d73c4a78d60b2cc1bca11b6d5e523969acfb5b756a3be1551b22239c79ae362b838b00669983c0caf6ed0c781d401c95d2b32857de8ce1b619dac4a75f200a5a5a5349543030304f347f4c12060904007f000703010202530500000000045f25060100000902015f2406010000090206655e732d060904007f0007030103028020b02baa51a94fac0954df204d61fe22da1d408d45db4aa1d70e600dad4faf6799732d060904007f0007030103018020c72e13582f01ba068dd1aac29a2428c0c54ab9c204fd53b3f13e8290e21e50f95f374083c5b441fec5b18efd1caa4a11b8e1cede0a8b42d442f00d7f604e429f339b4e3e6c06f9e76a2daa82c1722ee137a89038b969c634561581e6c26d9f6fa75c52
00:22:81:A4:53:80:0A:04:00:7F:00:07:02:02:02:02:03:83:0A:5A:5A:53:49:54:30:30:30:4F:34:91:20:88:E5:F2:C6:11:18:0D:0A:C1:0E:BD:E6:FC:2A:5E:62:41:79:C0:A5:77:C3:E4:88:52:DD:81:A4:CD:F7:90:51:67:17:73:15:06:09:04:00:7F:00:07:03:01:04:02:53:08:32:30:31:30:30:39:32:34
00:84:00:00:08
00820000400C9E7DB72CB0FAEA15B00FECAE0257546446A9395862239AF240C3C29E857F8403345817760FE13F6597F04D2F7330B59065F68DF71EF7FDEC86743CDE2869DD
00a4000c023f00
00:A4:02:0C:02:01:1D
00:b0:00:00:80
00B0008080
00b0010080
00:B0:01:80:80
00:b0:02:00:80
00B0028080
00b0030080
00:B0:03:80:80
00:b0:04:00:80
00B0048080
00b0050080
00:B0:05:80:80
00:b0:06:00:80
00B0068080
00b0070080
00:B0:07:80:80
00:22:41:a4:0c:80:0a:04:00:7f:00:07:02:02:03:02:02
00860000457C43804104239E3D05EEB059117D30F86AEB5AE7D12E0EBF758889C79115F2A13DC1BB570A5CAD91A384337C09D1B74BED1C0FF195A7C3EA3A2CEDF86DDEF7B95D1FD1B35D00
0020001006010203040506

View File

@@ -1 +0,0 @@
cvca-epass.cv

View File

@@ -1 +0,0 @@
cvca-eid.cv

View File

@@ -1 +0,0 @@
cvca-esign.cv

View File

@@ -1,14 +0,0 @@
CV_CERTS = DECVCAeID00102 DECVCAEPASS00102 DECVCAeSign00102
X509_CERTS = ff3d20d2
EXTRA_DIST = $(CV_CERTS) $(X509_CERTS)
install-data-local:
$(MKDIR_P) "$(DESTDIR)$(X509DIR)"
for cert in $(X509_CERTS); do $(INSTALL_DATA) $(srcdir)/$${cert} "$(DESTDIR)$(X509DIR)"; done
$(MKDIR_P) "$(DESTDIR)$(CVCDIR)"
for cert in $(CV_CERTS); do $(INSTALL_DATA) $(srcdir)/$${cert} "$(DESTDIR)$(CVCDIR)"; done
uninstall-local:
for cert in $(X509_CERTS); do rm -f "$(DESTDIR)$(X509DIR)/$${cert}"; done
for cert in $(CV_CERTS); do rm -f "$(DESTDIR)$(CVCDIR)/$${cert}"; done

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1 +0,0 @@
csca-germany_013_self_signed_cer.cer

View File

@@ -1,239 +0,0 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.63])
AC_INIT([nPA Smart Card Library], [0.7], [https://github.com/frankmorgner/vsmartcard/issues], [npa], [http://frankmorgner.github.io/vsmartcard/npa/README.html])
AC_CONFIG_SRCDIR([src/npa.c])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE
dnl Add libtool support.
ifdef(
[LT_INIT],
[
LT_INIT([win32-dll])
],
[
AC_LIBTOOL_WIN32_DLL
AC_PROG_LIBTOOL
]
)
# Checks for programs.
AC_PROG_CC
AC_PROG_CXX
AC_PROG_INSTALL
AC_PROG_SED
AM_PROG_CC_C_O
PKG_PROG_PKG_CONFIG
AC_ARG_VAR([HELP2MAN],
[absolute path to help2man used for man page generation of npa-tool])
AC_PATH_PROG(HELP2MAN, help2man, not found)
if test ! -r src/npa-tool.1 -a "${HELP2MAN}" = "not found"
then
if test "${HELP2MAN}" = "not found"
then
AC_MSG_ERROR([Need help2man to generate man page for npa-tool])
fi
else
# make sure these files are the most recent
touch src/npa-tool.1
fi
AC_ARG_VAR([GENGETOPT],
[absolute path to gengetopt used for command line parsing of npa-tool])
AC_PATH_PROG(GENGETOPT, gengetopt, not found)
if test ! -r src/cmdline.h -o ! -r src/cmdline.c
then
if test "${GENGETOPT}" = "not found"
then
AC_MSG_ERROR([Need gengetopt for parsing command line of npa-tool])
fi
else
# make sure these files are the most recent
touch src/npa-tool.ggo
touch src/cmdline.h src/cmdline.h
fi
# If you need to see the details, just run make V=1.
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
# Checks for libraries.
test -z "$OPENSC_LIBS" && OPENSC_LIBS="-lopensc"
test -z "$OPENSC_CFLAGS" && OPENSC_CFLAGS="-I${PWD}/${srcdir}/src/opensc/src -DENABLE_SM"
saved_CPPFLAGS="$CPPFLAGS"
saved_LIBS="$LIBS"
CPPFLAGS="$OPENSC_CFLAGS $CPPFLAGS"
LIBS="$LDFLAGS $OPENSC_LIBS"
AC_CHECK_HEADERS([libopensc/opensc.h libopensc/sm.h libopensc/pace.h],
[], [AC_MSG_ERROR([OpenSC headers not found])])
AC_MSG_CHECKING([for sc_sm_stop])
AC_TRY_LINK_FUNC(sc_sm_stop,
[ AC_MSG_RESULT([yes]) ],
[ AC_MSG_ERROR([Cannot link against libopensc with SM]) ])
AC_CHECK_FUNCS([sc_apdu_set_resp sc_apdu_get_octets _sc_match_atr list_iterator_next])
CPPFLAGS="$saved_CPPFLAGS"
LIBS="$saved_LIBS"
AC_SUBST([OPENSC_LIBS])
AC_SUBST([OPENSC_CFLAGS])
PKG_CHECK_EXISTS([libcrypto],
[PKG_CHECK_MODULES([OPENSSL], [libcrypto])],
[AC_MSG_WARN([libcrypto not found by pkg-config])])
saved_CPPFLAGS="$CPPFLAGS"
saved_LIBS="$LIBS"
CPPFLAGS="$CPPFLAGS $OPENSSL_CFLAGS"
LIBS="$LDFLAGS $OPENSSL_LIBS"
AC_CHECK_HEADERS(openssl/evp.h, [],
[ AC_MSG_ERROR([OpenSSL headers not found]) ])
AC_MSG_CHECKING([for EVP_read_pw_string_min])
AC_TRY_LINK_FUNC(EVP_read_pw_string_min,
[ AC_MSG_RESULT([yes]) ],
[ AC_MSG_ERROR([Cannot link against libcrypto]) ])
CPPFLAGS="$saved_CPPFLAGS"
LIBS="$saved_LIBS"
PKG_CHECK_EXISTS([libeac], [PKG_CHECK_MODULES([OPENPACE], [libeac >= 0.9])],
[AC_MSG_WARN([libeac not found by pkg-config])])
saved_CPPFLAGS="$CPPFLAGS"
saved_LIBS="$LIBS"
CPPFLAGS="$CPPFLAGS $OPENPACE_CFLAGS"
LIBS="$LDFLAGS $OPENPACE_LIBS"
AC_CHECK_HEADERS(eac/eac.h, [],
[ AC_MSG_ERROR([OpenPACE headers not found]) ])
AC_MSG_CHECKING([for EAC_CTX_init_pace])
AC_TRY_LINK_FUNC(EAC_CTX_init_pace, [ AC_MSG_RESULT([yes]) ],
[ AC_MSG_ERROR([Cannot link against libeac]) ])
CPPFLAGS="$saved_CPPFLAGS"
LIBS="$saved_LIBS"
AC_ARG_ENABLE(cvcdir,
AC_HELP_STRING([--enable-cvcdir=DIR],
[directory containing CV certificates (default is determined by libeac)]),
[cvcdir="${enableval}"],
[cvcdir=false])
if test "${cvcdir}" = false ; then
cvcdir="`$PKG_CONFIG libeac --variable=cvcdir`"
fi
if test "${cvcdir}" = "" ; then
AC_MSG_ERROR([use --enable-cvcdir=DIR])
fi
CVCDIR="${cvcdir}"
AC_SUBST(CVCDIR)
AC_ARG_ENABLE(x509dir,
AC_HELP_STRING([--enable-x509dir=DIR],
[directory containing X.509 certificates (default is determined by libeac)]),
[x509dir="${enableval}"],
[x509dir=false])
if test "${x509dir}" = false ; then
x509dir="`$PKG_CONFIG libeac --variable=x509dir`"
fi
if test -z "${x509dir}"
then
x509dir="`$PKG_CONFIG libeac --variable=x509dir`"
fi
if test -z "${x509dir}"
then
AC_MSG_ERROR([use --enable-x509dir=DIR])
fi
X509DIR="${x509dir}"
AC_SUBST(X509DIR)
PACKAGE_SUMMARY="Access the German electronic identity card (neuer Personalausweis/nPA)"
AC_SUBST(PACKAGE_SUMMARY)
# Checks for header files.
AC_CHECK_HEADERS([arpa/inet.h fcntl.h memory.h stdint.h stdlib.h string.h sys/ioctl.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_INLINE
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
AC_TYPE_UINT8_T
# Checks for library functions.
AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CHECK_FUNCS([getline memmove memset strerror gettimeofday strtol strlcpy])
AC_SUBST(DYN_LIB_EXT)
case "$host" in
*-*-darwin*)
DYN_LIB_EXT="dylib"
;;
*-*-freebsd*)
DYN_LIB_EXT="so"
;;
*-*-openbsd*)
DYN_LIB_EXT="so.0.0"
;;
*-*-solaris*)
DYN_LIB_EXT="so"
;;
*-*-dragonfly*)
DYN_LIB_EXT="so"
;;
*-*-cygwin*)
DYN_LIB_EXT="dll"
;;
*)
DYN_LIB_EXT="so"
esac
cat << EOF
${PACKAGE} has been configured with following options:
Version: ${PACKAGE_VERSION}
User binaries: $(eval eval eval echo "${bindir}")
Libraries: $(eval eval eval echo "${libdir}")
Configuration files: $(eval eval eval echo "${sysconfdir}")
Host: ${host}
Compiler: ${CC}
Preprocessor flags: ${CPPFLAGS}
Compiler flags: ${CFLAGS}
Preprocessor flags: ${CPPFLAGS}
Linker flags: ${LDFLAGS}
Libraries: ${LIBS}
OPENSC_LIBS: ${OPENSC_LIBS}
OPENSC_CFLAGS: ${OPENSC_CFLAGS}
OPENSSL_CFLAGS: ${OPENSSL_CFLAGS}
OPENSSL_LIBS: ${OPENSSL_LIBS}
OPENPACE_CFLAGS: ${OPENPACE_CFLAGS}
OPENPACE_LIBS: ${OPENPACE_LIBS}
HELP2MAN: ${HELP2MAN}
GENGETOPT: ${GENGETOPT}
EOF
AC_CONFIG_FILES([
Makefile
certs/Makefile
doc/Makefile
m4/Makefile
src/Makefile
cross/Makefile
])
AC_OUTPUT

View File

@@ -1,13 +0,0 @@
#!/bin/bash
if [ -e "/Library/OpenSC/etc/opensc.conf" ]
then
mv /Library/OpenSC/etc/opensc.conf /Library/OpenSC/etc/opensc.conf.orig
fi
mkdir -p /Library/OpenSC/etc
ln -sf /Library/nPA/etc/opensc.conf /Library/OpenSC/etc
for f in /Library/nPA/bin/*
do
ln -sf $f /usr/local/bin
done

View File

@@ -1,20 +0,0 @@
#!/bin/bash
if [ -L "/Library/OpenSC/etc/opensc.conf" ]
then
if [ "$(readlink /Library/OpenSC/etc/opensc.conf)" == "/Library/nPA/etc/opensc.conf" ]
then
rm /Library/OpenSC/etc/opensc.conf
mv /Library/OpenSC/etc/opensc.conf.orig /Library/OpenSC/etc/opensc.conf
fi
fi
# Remove symlinks to commands
for file in /Library/nPA/bin/*; do
test -L "/usr/local/bin/$(basename $file)" && rm -f "/usr/local/bin/$(basename $file)"
done
# Remove installed files
rm -rf /Library/nPA
echo "nPA Smart Card Library has been removed from your system. See you again!"

View File

@@ -1,181 +0,0 @@
EXTRA_DIST=opensc.conf_win32
all-local:
@echo Use \`$(MAKE) win\` to cross compile for Windows
@echo Use \`$(MAKE) android\` to cross compile for Android
OSX_TOOLS_DIR = "$(shell xcode-select -p)/Platforms/MacOSX.platform/Developer/SDKs"
OSX_TOOL_DIR ?= "$(OSX_TOOLS_DIR)/$(shell ls -1 $(OSX_TOOLS_DIR) | sort -n -k2 -t. -r | head -1)/usr"
OSX_TARGETDIR = $(abs_top_builddir)/$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)_osx
osx: $(OPENPACE_MAKEFILE) $(OPENSC_CONFIGURE)
@echo Compiling OpenPACE
$(MAKE) -C $(OPENPACE) osx
rm -rf $(OSX_TARGETDIR) dmg $(top_builddir)/$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)_osx.dmg
cp -r $(OPENPACE)/*_osx $(OSX_TARGETDIR)
@echo Configuring OpenSC
grep _sc_card_add_ec_alg $(OPENSC)/src/libopensc/libopensc.exports || echo _sc_card_add_ec_alg >> $(OPENSC)/src/libopensc/libopensc.exports
grep _sc_card_add_rsa_alg $(OPENSC)/src/libopensc/libopensc.exports || echo _sc_card_add_rsa_alg >> $(OPENSC)/src/libopensc/libopensc.exports
grep _sc_match_atr $(OPENSC)/src/libopensc/libopensc.exports || echo _sc_match_atr >> $(OPENSC)/src/libopensc/libopensc.exports
grep sc_apdu_set_resp $(OPENSC)/src/libopensc/libopensc.exports || echo sc_apdu_set_resp >> $(OPENSC)/src/libopensc/libopensc.exports
grep sc_apdu_get_octets $(OPENSC)/src/libopensc/libopensc.exports || echo sc_apdu_get_octets >> $(OPENSC)/src/libopensc/libopensc.exports
grep sc_pkcs15_pincache_add $(OPENSC)/src/libopensc/libopensc.exports || echo sc_pkcs15_pincache_add >> $(OPENSC)/src/libopensc/libopensc.exports
@echo Compiling OpenSC
cd $(OPENSC) && OPENSSL_LIBS="-L$(OSX_TARGETDIR)/Library/OpenPACE/lib -lcrypto" OPENSSL_CFLAGS="-I$(OSX_TARGETDIR)/Library/OpenPACE/include" MacOSX/build
@echo Configuring libnpa
cd $(top_builddir) && ./configure --prefix=/Library/nPA \
PKG_CONFIG_SYSROOT_DIR=$(OSX_TARGETDIR) \
PKG_CONFIG_PATH=$(OSX_TARGETDIR)/Library/OpenPACE/lib/pkgconfig \
OPENSC_LIBS="-L$(OPENSC)/target/Library/OpenSC/lib -lopensc" \
CFLAGS="-arch i386 -arch x86_64"
@echo Compiling libnpa
make -C $(top_builddir) install DESTDIR=$(OSX_TARGETDIR)
mkdir -p $(OSX_TARGETDIR)/Library/nPA/etc
cp $(top_builddir)/opensc.conf $(OSX_TARGETDIR)/Library/nPA/etc/opensc.conf
rm -rf $(OSX_TARGETDIR)/Library/OpenPACE
pkgbuild --root $(OSX_TARGETDIR) --scripts MacOSX_install --identifier com.vsmartcard.npa.mac --version $(PACKAGE_VERSION) --install-location / nPA-$(PACKAGE_VERSION)_install.pkg
pkgbuild --nopayload --identifier com.vsmartcard.npa.mac.uninstall --scripts MacOSX_uninstall Uninstall_nPA.pkg
mkdir -p dmg
cp *.pkg dmg
cp $(OPENPACE)/cross/*.pkg dmg
cp $(OPENSC)/OpenSC-*.pkg dmg
cp $(OPENSC)/Uninstall_OpenSC.pkg dmg
hdiutil create -srcfolder dmg -volname "$(PACKAGE_STRING) for Mac OS X" $(top_builddir)/$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)_osx.dmg
WIN_TOOL ?= i686-w64-mingw32
WIN_TOOL_DIR ?= /usr/$(WIN_TOOL)
WIN_TARGETDIR = $(abs_top_builddir)/$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)_win32
win:
$(MAKE) cross \
TOOL=$(WIN_TOOL) \
TOOL_DIR=$(WIN_TOOL_DIR) \
TARGETDIR=$(WIN_TARGETDIR) \
LDFLAGS="-static-libgcc" \
LIBEAC=$(WIN_TARGETDIR)/bin/libeac-0.dll \
LIBNPA=$(WIN_TARGETDIR)/bin/libnpa-0.dll
cp opensc.conf_win32 $(WIN_TARGETDIR)/etc/opensc.conf
ANDROID_ARCH ?= arm
ANDROID_TOOL ?= $(ANDROID_ARCH)-linux-androideabi
ANDROID_TOOL_DIR ?= $(abs_builddir)/ndk
ANDROID_TARGETDIR = $(abs_top_builddir)/$(PACKAGE_TARNAME)-$(PACKAGE_VERSION)_$(ANDROID_ARCH)-androideabi
MAKE_STANDALONE_TOOLCHAIN ?= $${HOME}/.local/opt/android-ndk-r9/build/tools/make-standalone-toolchain.sh
android: $(VIRTUALSMARTCARD_CONFIGURE)
test -d $(ANDROID_TOOL_DIR) \
|| $(MAKE_STANDALONE_TOOLCHAIN) --install-dir=$(ANDROID_TOOL_DIR) --arch=$(ANDROID_ARCH) --platform=android-16
cd $(VIRTUALSMARTCARD) && env PATH=$(ANDROID_TOOL_DIR)/bin:$${PATH} ./configure \
--prefix=/ \
--host=$(ANDROID_TOOL) --target=$(ANDROID_TOOL) \
--enable-libpcsclite --enable-vpcdhost=localhost \
PKG_CONFIG_SYSROOT_DIR=$(ANDROID_TARGETDIR) \
PKG_CONFIG_LIBDIR=$(ANDROID_TARGETDIR)/lib/pkgconfig \
PKG_CONFIG_PATH=$(ANDROID_TARGETDIR)/lib/pkgconfig \
CFLAGS="-I$(ANDROID_TOOL_DIR)/include -fPIE" \
LDFLAGS="-L$(ANDROID_TOOL_DIR)/lib -fPIE -pie"
@echo Compiling virtual smartcard
env PATH=$(ANDROID_TOOL_DIR)/bin:$${PATH} $(MAKE) -C $(VIRTUALSMARTCARD) install DESTDIR=$(ANDROID_TARGETDIR)
env PATH=$(ANDROID_TOOL_DIR)/bin:$${PATH} $(MAKE) cross \
TOOL=$(ANDROID_TOOL) \
TOOL_DIR=$(ANDROID_TOOL_DIR) \
TARGETDIR=$(ANDROID_TARGETDIR) \
LIBEAC=$(ANDROID_TARGETDIR)/lib/libeac.so \
LIBNPA=$(ANDROID_TARGETDIR)/lib/libnpa.so
@echo Configuring virtual smartcard
clean-local:
rm -rf $(TARGETDIR)
cross: $(LIBNPA)
$(LIBNPA): $(OPENPACE_MAKEFILE) $(OPENSC_CONFIGURE)
@echo Compiling OpenPACE
env ac_cv_func_malloc_0_nonnull=yes $(MAKE) -C $(OPENPACE)/cross cross \
TOOL=$(TOOL) \
TOOL_DIR=$(TOOL_DIR) \
TARGETDIR=$(TARGETDIR) \
LDFLAGS="$${LDFLAGS}" \
CFLAGS="$${CFLAGS}" \
LIBEAC=$(LIBEAC)
@echo Configuring OpenSC
grep _sc_card_add_ec_alg $(OPENSC)/src/libopensc/libopensc.exports || echo _sc_card_add_ec_alg >> $(OPENSC)/src/libopensc/libopensc.exports
grep _sc_card_add_rsa_alg $(OPENSC)/src/libopensc/libopensc.exports || echo _sc_card_add_rsa_alg >> $(OPENSC)/src/libopensc/libopensc.exports
grep _sc_match_atr $(OPENSC)/src/libopensc/libopensc.exports || echo _sc_match_atr >> $(OPENSC)/src/libopensc/libopensc.exports
grep sc_apdu_set_resp $(OPENSC)/src/libopensc/libopensc.exports || echo sc_apdu_set_resp >> $(OPENSC)/src/libopensc/libopensc.exports
grep sc_apdu_get_octets $(OPENSC)/src/libopensc/libopensc.exports || echo sc_apdu_get_octets >> $(OPENSC)/src/libopensc/libopensc.exports
grep sc_pkcs15_pincache_add $(OPENSC)/src/libopensc/libopensc.exports || echo sc_pkcs15_pincache_add >> $(OPENSC)/src/libopensc/libopensc.exports
cd $(OPENSC) && ./configure \
--prefix=/ \
--host=$(TOOL) --target=$(TOOL) \
--enable-sm \
PKG_CONFIG_SYSROOT_DIR=$(TARGETDIR) \
PKG_CONFIG_LIBDIR=$(TARGETDIR)/lib/pkgconfig \
PKG_CONFIG_PATH=$(TARGETDIR)/lib/pkgconfig \
CFLAGS="$${CFLAGS} -I$(TOOL_DIR)/include -I$(TARGETDIR)/include" \
LDFLAGS="$${LDFLAGS} -L$(TOOL_DIR)/lib"
@echo Compiling OpenSC
$(MAKE) -C $(OPENSC) install DESTDIR=$(TARGETDIR)
@echo Configuring libnpa
cd $(top_builddir) && env ac_cv_func_malloc_0_nonnull=yes ac_cv_func_realloc_0_nonnull=yes ./configure \
--prefix=/ \
--host=$(TOOL) --target=$(TOOL) \
PKG_CONFIG_SYSROOT_DIR=$(TARGETDIR) \
PKG_CONFIG_LIBDIR=$(TARGETDIR)/lib/pkgconfig \
PKG_CONFIG_PATH=$(TARGETDIR)/lib/pkgconfig \
OPENSC_LIBS="-L$(TARGETDIR)/lib -lopensc" \
CFLAGS="$${CFLAGS} -I$(TOOL_DIR)/include -I$(TARGETDIR)/include" \
LDFLAGS="$${LDFLAGS} -L$(TOOL_DIR)/lib"
@echo Compiling libnpa
env CROSS_COMPILE=$(CROSS_COMPILE) \
$(MAKE) -C $(top_builddir) install DESTDIR=$(TARGETDIR)
OPENPACE=$(abs_builddir)/openpace
OPENPACE_CONFIGURE_AC=$(OPENPACE)/configure.ac
OPENPACE_CONFIGURE=$(OPENPACE)/configure
OPENPACE_MAKEFILE=$(OPENPACE)/Makefile
$(OPENPACE_CONFIGURE_AC):
git clone https://github.com/frankmorgner/openpace.git $(OPENPACE)
$(OPENPACE_CONFIGURE): $(OPENPACE_CONFIGURE_AC)
cd $(OPENPACE) && autoreconf --verbose --install --symlink
$(OPENPACE_MAKEFILE): $(OPENPACE_CONFIGURE)
cd $(OPENPACE) && ./configure --enable-openssl-install
OPENSC=$(abs_builddir)/opensc
OPENSC_CONFIGURE_AC=$(OPENSC)/configure.ac
OPENSC_CONFIGURE=$(OPENSC)/configure
$(OPENSC_CONFIGURE_AC):
git clone git://github.com/OpenSC/OpenSC.git $(OPENSC)
$(OPENSC_CONFIGURE): $(OPENSC_CONFIGURE_AC)
cd $(OPENSC) && autoreconf --verbose --install --symlink
VSMARTCARD=$(abs_builddir)/vsmartcard
VIRTUALSMARTCARD=$(VSMARTCARD)/virtualsmartcard
VIRTUALSMARTCARD_CONFIGURE_AC=$(VIRTUALSMARTCARD)/configure.ac
VIRTUALSMARTCARD_CONFIGURE=$(VIRTUALSMARTCARD)/configure
$(VIRTUALSMARTCARD_CONFIGURE_AC):
git clone git://github.com/frankmorgner/vsmartcard.git $(VSMARTCARD)
$(VIRTUALSMARTCARD_CONFIGURE): $(VIRTUALSMARTCARD_CONFIGURE_AC)
cd $(VIRTUALSMARTCARD) && autoreconf --verbose --install --symlink

View File

@@ -1,41 +0,0 @@
app default {
# What card drivers to load at start-up
#
# A special value of 'internal' will load all
# statically linked drivers. If an unknown (ie. not
# internal) driver is supplied, a separate configuration
# configuration block has to be written for the driver.
# Default: internal
# NOTE: When "internal" keyword is used, must be last entry
#
card_drivers = npa, internal;
# Card driver configuration blocks.
#
# For card drivers loaded from an external shared library/DLL,
# you need to specify the path name of the module
#
card_driver npa {
# The location of the driver library
module = libcardnpa-0.dll;
}
# PKCS #15
framework pkcs15 {
# additional settings per driver
#
# For pkcs15 emulators loaded from an external shared
# library/DLL, you need to specify the path name of the module
# and customize the card_atr example above correctly.
#
emulate npa {
# The location of the driver library
module = libpkcs15npa-0.dll;
function = sc_pkcs15emu_npa_init_ex;
can = 222222;
}
}
}

View File

@@ -1,12 +0,0 @@
FILE_PATTERNS = *.h
GENERATE_HTML = YES
GENERATE_LATEX = NO
GENERATE_TAGFILE = @builddir@/@PACKAGE@.tag
GENERATE_XML = YES
INPUT = @top_srcdir@/src/npa
OPTIMIZE_OUTPUT_FOR_C = YES
OUTPUT_DIRECTORY = @builddir@
PROJECT_NAME = @PACKAGE_NAME@
PROJECT_NUMBER = @PACKAGE_VERSION@
STRIP_FROM_PATH = @top_srcdir@/src
XML_OUTPUT = xml

View File

@@ -1,35 +0,0 @@
do_subst = $(SED) \
-e 's,[@]PACKAGE[@],$(PACKAGE),g' \
-e 's,[@]PACKAGE_NAME[@],$(PACKAGE_NAME),g' \
-e 's,[@]PACKAGE_TARNAME[@],$(PACKAGE_TARNAME),g' \
-e 's,[@]PACKAGE_BUGREPORT[@],$(PACKAGE_BUGREPORT),g' \
-e 's,[@]PACKAGE_URL[@],$(PACKAGE_URL),g' \
-e 's,[@]PACKAGE_SUMMARY[@],$(PACKAGE_SUMMARY),g' \
-e 's,[@]PACKAGE_VERSION[@],$(PACKAGE_VERSION),g' \
-e 's,[@]RECOMMENDED_CONF_OPTS[@],,g' \
-e 's,[@]builddir[@],$(builddir),g' \
-e 's,[@]prefix[@],$(prefix),g' \
-e 's,[@]exec_prefix[@],$(exec_prefix),g' \
-e 's,[@]libdir[@],$(libdir),g' \
-e 's,[@]includedir[@],$(includedir),g' \
-e 's,[@]VERSION[@],$(VERSION),g' \
-e 's,[@]OPENSC_LIBS[@],$(OPENSC_LIBS),g' \
-e 's,[@]abs_top_srcdir[@],$(abs_top_srcdir),g' \
-e 's,[@]top_srcdir[@],$(top_srcdir),g'
EXTRA_DIST = README.txt.in README.txt Doxyfile.in example.c autotools.txt autotools.txt.in questions.txt questions.txt.in download.txt.in download.txt
DOXYGEN = doxygen
doc: $(top_srcdir)/src/npa/*.h *.in
$(do_subst) < Doxyfile.in > Doxyfile
$(DOXYGEN) Doxyfile
touch api.txt
$(do_subst) < autotools.txt.in > autotools.txt
$(do_subst) < questions.txt.in > questions.txt
$(do_subst) < download.txt.in > download.txt
$(do_subst) < README.txt.in > README.txt
touch doc
clean-local:
rm -f doc Doxyfile

View File

@@ -1,253 +0,0 @@
.. highlight:: sh
.. |PACE| replace:: :abbr:`PACE (Password Authenticated Connection Establishment)`
.. |TA| replace:: :abbr:`TA (Terminal Authenticatation)`
.. |CA| replace:: :abbr:`CA (Chip Authentication)`
.. |EAC| replace:: :abbr:`EAC (Extended Access Control)`
.. |CSCA| replace:: :abbr:`CSCA (Country Signing Certificate Authority)`
.. |npa-tool| replace:: :command:`npa-tool`
.. _libnpa:
################################################################################
nPA Smart Card Library
################################################################################
.. sidebar:: Summary
Access the German electronic identity card (neuer Personalausweis/nPA)
:Author:
`Frank Morgner <frankmorgner@gmail.com>`_
:License:
GPL version 3
:Tested Platforms:
- Windows
- Mac OS X
- Linux (Debian, Ubuntu, OpenMoko)
The nPA Smart Card Library offers an easy to use API for the new German identity card
(neuer Personalausweis, nPA). The library also implements secure messaging,
which could also be used for other cards. The included |npa-tool| can
be used for PIN management or to send APDUs inside a secure channel.
The nPA Smart Card Library is implemented using OpenPACE_ and OpenSC_. nPA Smart Card Library
implements and initializes Secure Messaging wrappers of OpenSC to allow a
transparent SM usage in OpenSC. This allows nPA Smart Card Library to be fully
compatible with OpenSC.
.. tikz:: Architecture of the nPA Smart Card Library
:stringsubst:
:libs: arrows, calc, fit, patterns, plotmarks, shapes.geometric, shapes.misc, shapes.symbols, shapes.arrows, shapes.callouts, shapes.multipart, shapes.gates.logic.US, shapes.gates.logic.IEC, er, automata, backgrounds, chains, topaths, trees, petri, mindmap, matrix, calendar, folding, fadings, through, positioning, scopes, decorations.fractals, decorations.shapes, decorations.text, decorations.pathmorphing, decorations.pathreplacing, decorations.footprints, decorations.markings, shadows
\input{%(wd)s/bilder/tikzstyles.tex}
\node (npa-tool) [aktivbox] {\texttt{npa-tool}};
\node (libnpa) [box, below=of npa-tool] {\texttt{libnpa}};
\node (opensc)
[klein, box, shape=rectangle split, rectangle split parts=2, right=of libnpa,
kleiner, yshift=-1cm]
{OpenSC (\texttt{libopensc})
\nodepart{second}
\footnotesize PC/SC\qquad CT-API
};
\draw [box] ($(opensc.text split)-(.05cm,0)$) -- ($(opensc.south)-(.05cm,0)$);
\node (openpace) [klein, box, left=of libnpa, yshift=-1cm] {OpenPACE};
\begin{pgfonlayer}{background}
\path[linie]
(npa-tool) edge (libnpa)
(libnpa) edge (opensc)
(libnpa) edge (openpace);
\end{pgfonlayer}
The nPA Smart Card Library has the following dependencies:
- OpenPACE_
- OpenSC_
- OpenSSL_
.. include:: download.txt
.. _npa-install:
.. include:: autotools.txt
====================================
Installation of OpenPACE and OpenSSL
====================================
The nPA Smart Card Library links against OpenSSL, which must be patched for OpenPACE.
Here is an example of how to get the standard installation of OpenPACE (with
the required binaries for OpenSSL)::
PREFIX=/tmp/install
OPENPACE=$PWD/openpace
git clone https://github.com/frankmorgner/openpace.git $OPENPACE
cd $OPENPACE
autoreconf --verbose --install
# with `--enable-openssl-install` OpenSSL will be downloaded and installed along with OpenPACE
./configure --enable-openssl-install --prefix=$PREFIX
make install && cd -
The file :file:`libcrypto.pc` should be located in ``$INSTALL/lib/pkgconfig``.
======================
Installation of OpenSC
======================
The nPA Smart Card Library needs the OpenSC components to be installed (especially
:file:`libopensc.so`). Here is an example of how to get a suitable installation
of OpenSC::
VSMARTCARD=$PWD/vsmartcard
git clone https://github.com/frankmorgner/vsmartcard.git $VSMARTCARD
cd $VSMARTCARD
git submodule init
git submodule update
cd $VSMARTCARD/npa/src/opensc
autoreconf --verbose --install
# adding PKG_CONFIG_PATH here lets OpenSC use the patched OpenSSL
./configure --prefix=$PREFIX PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig --enable-sm
make install && cd -
Now :file:`libopensc.so` should be located in ``$PREFIX/lib``.
================================================================================
Installation of the nPA Smart Card Library
================================================================================
To complete this step-by-step guide, here is how to install nPA Smart Card Library::
cd $VSMARTCARD/npa
autoreconf --verbose --install
./configure --prefix=$PREFIX PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig OPENSC_LIBS="-L$PREFIX/lib -lopensc -lcrypto"
make install && cd -
.. _npa-usage:
********************************************************************************
Using the nPA Smart Card Library
********************************************************************************
The API to libnpa is documented in :ref:`npa-api`. It includes a simple
programming example. Here we will focus on the command line interface to the
library offered by the |npa-tool|. It can perform |EAC| (i.e. |PACE|, |TA|,
|CA|) and read data groups from the identity card.
To pass a secret to |npa-tool| for |PACE|, command line parameters or
environment variables can be used. If the smart card reader supports |PACE|,
its PIN pad is used. If none of these options apply, |npa-tool| will show a
password prompt.
The certificates certificate chain for |TA| should be passed in the correct
order (finishing with the terminal certificate) so that the card can verify it.
|CA| is always done when the terminal's signature has been verified
successfully. The appropriate |CSCA| certificate will automatically be looked
up by OpenPACE.
|npa-tool| can send arbitrary APDUs to the nPA in the secure channel (after
|PACE| or |EAC|). APDUs are entered interactively or through a file. APDUs
are formatted in hex (upper or lower case) with an optional colon to separate
the bytes. Example APDUs can be found in :file:`apdus`.
.. program-output:: npa-tool --help
======================
Linking against libnpa
======================
Following the section Installation_ above, you have installed OpenSSL,
OpenPACE, OpenSC and the nPA Smart Card Library to `$PREFIX` which points to
:file:`/tmp/install`. To compile a program using nPA Smart Card Library you also need
the OpenSC header files, which are located in
:file:`$VSMARTCARD/npa/src/opensc` Here is how to compile an external program
with these libraries::
PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig
cc example.c -I$VSMARTCARD/npa/src/opensc \
$(pkg-config --cflags --libs npa)
Alternatively you can specify libraries and flags by hand::
cc example.c -I$VSMARTCARD/npa/src/opensc \
-I$PREFIX/include \
-L$PREFIX/lib -lnpa -lopensc -lcrypto"
********************************************************************************
Using the German identity card with OpenSC
********************************************************************************
.. versionadded:: 0.6
Added external card driver and PKCS#15 emulator for supporting nPA in
OpenSC.
To let OpenSC recognize the German ID card we implemented an external card
driver. We supply a sample :file:`opensc.conf` which adds our driver to all
components of OpenSC. Load it by setting :envvar:`OPENSC_CONF`::
export OPENSC_CONF=$VSMARTCARD/npa/opensc.conf
On Windows you need to use :command:`set` instead of :command:`export`. In
:file:`npa-0.7_win32` do the following::
cd bin
set OPENSC_CONF=..\etc\opensc.conf
The card driver recognizes the PIN verification method by it's ID. MRZ, CAN,
eID-PIN and PUK are verified using |PACE| (ID ``0x01``, ``0x02``, ``0x03`` and
``0x04``). The eSign PIN (ID ``0x83``) is verified using a standard ISO-7816-4
VERIFY command. Here, for example, we show how to verify the eID-PIN "123456"
with :command:`opensc-explorer`::
opensc-explorer
OpenSC [3F00]> verify CHV3 313233343536
Code correct.
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
suspend the eID-PIN after the second try of verification. To unlock the last
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
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
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. An
intializesed card can be used for electronic signature, with a Comfort Reader
(KAT-K) [#footnote1]_ that authenticates as signature terminal to the card.
Below, you can see two examples how to create a signature::
# SHA256_FILE is set to a file of 32 bytes length
# QES PIN will be prompted by the reader
pkcs15-crypt --sign --sha-256 --input $SHA256_FILE
pkcs11-tool --module opensc-pkcs11.so --sign --input-file $SHA256_FILE
.. include:: questions.txt
********************
Notes and References
********************
.. target-notes::
.. _OpenPACE: https://frankmorgner.github.io/openpace/
.. _OpenSC: https://github.com/OpenSC/OpenSC
.. _OpenSSL: http://www.openssl.org
.. _sign-me: https://www.bundesdruckerei.de/en/798-sign-me
.. _BSI TR-03119: https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TR03119/BSI-TR-03119_V1_pdf.pdf
.. _Reiner SCT cyberJack RFID komfort: http://www.reiner-sct.com/produkte/chipkartenleser/cyberJack_RFID_komfort.html
.. [#footnote1] A Comfort Reader is defined by `BSI TR-03119`_. Today the `Reiner SCT cyberJack RFID komfort`_ is the only reader that supports this configuration.

View File

@@ -1,253 +0,0 @@
.. highlight:: sh
.. |PACE| replace:: :abbr:`PACE (Password Authenticated Connection Establishment)`
.. |TA| replace:: :abbr:`TA (Terminal Authenticatation)`
.. |CA| replace:: :abbr:`CA (Chip Authentication)`
.. |EAC| replace:: :abbr:`EAC (Extended Access Control)`
.. |CSCA| replace:: :abbr:`CSCA (Country Signing Certificate Authority)`
.. |npa-tool| replace:: :command:`npa-tool`
.. _libnpa:
################################################################################
@PACKAGE_NAME@
################################################################################
.. sidebar:: Summary
@PACKAGE_SUMMARY@
:Author:
`Frank Morgner <frankmorgner@gmail.com>`_
:License:
GPL version 3
:Tested Platforms:
- Windows
- Mac OS X
- Linux (Debian, Ubuntu, OpenMoko)
The @PACKAGE_NAME@ offers an easy to use API for the new German identity card
(neuer Personalausweis, nPA). The library also implements secure messaging,
which could also be used for other cards. The included |npa-tool| can
be used for PIN management or to send APDUs inside a secure channel.
The @PACKAGE_NAME@ is implemented using OpenPACE_ and OpenSC_. @PACKAGE_NAME@
implements and initializes Secure Messaging wrappers of OpenSC to allow a
transparent SM usage in OpenSC. This allows @PACKAGE_NAME@ to be fully
compatible with OpenSC.
.. tikz:: Architecture of the @PACKAGE_NAME@
:stringsubst:
:libs: arrows, calc, fit, patterns, plotmarks, shapes.geometric, shapes.misc, shapes.symbols, shapes.arrows, shapes.callouts, shapes.multipart, shapes.gates.logic.US, shapes.gates.logic.IEC, er, automata, backgrounds, chains, topaths, trees, petri, mindmap, matrix, calendar, folding, fadings, through, positioning, scopes, decorations.fractals, decorations.shapes, decorations.text, decorations.pathmorphing, decorations.pathreplacing, decorations.footprints, decorations.markings, shadows
\input{%(wd)s/bilder/tikzstyles.tex}
\node (npa-tool) [aktivbox] {\texttt{npa-tool}};
\node (libnpa) [box, below=of npa-tool] {\texttt{libnpa}};
\node (opensc)
[klein, box, shape=rectangle split, rectangle split parts=2, right=of libnpa,
kleiner, yshift=-1cm]
{OpenSC (\texttt{libopensc})
\nodepart{second}
\footnotesize PC/SC\qquad CT-API
};
\draw [box] ($(opensc.text split)-(.05cm,0)$) -- ($(opensc.south)-(.05cm,0)$);
\node (openpace) [klein, box, left=of libnpa, yshift=-1cm] {OpenPACE};
\begin{pgfonlayer}{background}
\path[linie]
(npa-tool) edge (libnpa)
(libnpa) edge (opensc)
(libnpa) edge (openpace);
\end{pgfonlayer}
The @PACKAGE_NAME@ has the following dependencies:
- OpenPACE_
- OpenSC_
- OpenSSL_
.. include:: download.txt
.. _npa-install:
.. include:: autotools.txt
====================================
Installation of OpenPACE and OpenSSL
====================================
The @PACKAGE_NAME@ links against OpenSSL, which must be patched for OpenPACE.
Here is an example of how to get the standard installation of OpenPACE (with
the required binaries for OpenSSL)::
PREFIX=/tmp/install
OPENPACE=$PWD/openpace
git clone https://github.com/frankmorgner/openpace.git $OPENPACE
cd $OPENPACE
autoreconf --verbose --install
# with `--enable-openssl-install` OpenSSL will be downloaded and installed along with OpenPACE
./configure --enable-openssl-install --prefix=$PREFIX
make install && cd -
The file :file:`libcrypto.pc` should be located in ``$INSTALL/lib/pkgconfig``.
======================
Installation of OpenSC
======================
The @PACKAGE_NAME@ needs the OpenSC components to be installed (especially
:file:`libopensc.so`). Here is an example of how to get a suitable installation
of OpenSC::
VSMARTCARD=$PWD/vsmartcard
git clone https://github.com/frankmorgner/vsmartcard.git $VSMARTCARD
cd $VSMARTCARD
git submodule init
git submodule update
cd $VSMARTCARD/npa/src/opensc
autoreconf --verbose --install
# adding PKG_CONFIG_PATH here lets OpenSC use the patched OpenSSL
./configure --prefix=$PREFIX PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig --enable-sm
make install && cd -
Now :file:`libopensc.so` should be located in ``$PREFIX/lib``.
================================================================================
Installation of the @PACKAGE_NAME@
================================================================================
To complete this step-by-step guide, here is how to install @PACKAGE_NAME@::
cd $VSMARTCARD/npa
autoreconf --verbose --install
./configure --prefix=$PREFIX PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig OPENSC_LIBS="-L$PREFIX/lib -lopensc -lcrypto"
make install && cd -
.. _npa-usage:
********************************************************************************
Using the @PACKAGE_NAME@
********************************************************************************
The API to libnpa is documented in :ref:`npa-api`. It includes a simple
programming example. Here we will focus on the command line interface to the
library offered by the |npa-tool|. It can perform |EAC| (i.e. |PACE|, |TA|,
|CA|) and read data groups from the identity card.
To pass a secret to |npa-tool| for |PACE|, command line parameters or
environment variables can be used. If the smart card reader supports |PACE|,
its PIN pad is used. If none of these options apply, |npa-tool| will show a
password prompt.
The certificates certificate chain for |TA| should be passed in the correct
order (finishing with the terminal certificate) so that the card can verify it.
|CA| is always done when the terminal's signature has been verified
successfully. The appropriate |CSCA| certificate will automatically be looked
up by OpenPACE.
|npa-tool| can send arbitrary APDUs to the nPA in the secure channel (after
|PACE| or |EAC|). APDUs are entered interactively or through a file. APDUs
are formatted in hex (upper or lower case) with an optional colon to separate
the bytes. Example APDUs can be found in :file:`apdus`.
.. program-output:: npa-tool --help
======================
Linking against libnpa
======================
Following the section Installation_ above, you have installed OpenSSL,
OpenPACE, OpenSC and the @PACKAGE_NAME@ to `$PREFIX` which points to
:file:`/tmp/install`. To compile a program using @PACKAGE_NAME@ you also need
the OpenSC header files, which are located in
:file:`$VSMARTCARD/npa/src/opensc` Here is how to compile an external program
with these libraries::
PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig
cc example.c -I$VSMARTCARD/npa/src/opensc \
$(pkg-config --cflags --libs npa)
Alternatively you can specify libraries and flags by hand::
cc example.c -I$VSMARTCARD/npa/src/opensc \
-I$PREFIX/include \
-L$PREFIX/lib -lnpa -lopensc -lcrypto"
********************************************************************************
Using the German identity card with OpenSC
********************************************************************************
.. versionadded:: 0.6
Added external card driver and PKCS#15 emulator for supporting nPA in
OpenSC.
To let OpenSC recognize the German ID card we implemented an external card
driver. We supply a sample :file:`opensc.conf` which adds our driver to all
components of OpenSC. Load it by setting :envvar:`OPENSC_CONF`::
export OPENSC_CONF=$VSMARTCARD/npa/opensc.conf
On Windows you need to use :command:`set` instead of :command:`export`. In
:file:`@PACKAGE_TARNAME@-@PACKAGE_VERSION@_win32` do the following::
cd bin
set OPENSC_CONF=..\etc\opensc.conf
The card driver recognizes the PIN verification method by it's ID. MRZ, CAN,
eID-PIN and PUK are verified using |PACE| (ID ``0x01``, ``0x02``, ``0x03`` and
``0x04``). The eSign PIN (ID ``0x83``) is verified using a standard ISO-7816-4
VERIFY command. Here, for example, we show how to verify the eID-PIN "123456"
with :command:`opensc-explorer`::
opensc-explorer
OpenSC [3F00]> verify CHV3 313233343536
Code correct.
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
suspend the eID-PIN after the second try of verification. To unlock the last
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
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
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. An
intializesed card can be used for electronic signature, with a Comfort Reader
(KAT-K) [#footnote1]_ that authenticates as signature terminal to the card.
Below, you can see two examples how to create a signature::
# SHA256_FILE is set to a file of 32 bytes length
# QES PIN will be prompted by the reader
pkcs15-crypt --sign --sha-256 --input $SHA256_FILE
pkcs11-tool --module opensc-pkcs11.so --sign --input-file $SHA256_FILE
.. include:: questions.txt
********************
Notes and References
********************
.. target-notes::
.. _OpenPACE: https://frankmorgner.github.io/openpace/
.. _OpenSC: https://github.com/OpenSC/OpenSC
.. _OpenSSL: http://www.openssl.org
.. _sign-me: https://www.bundesdruckerei.de/en/798-sign-me
.. _BSI TR-03119: https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TR03119/BSI-TR-03119_V1_pdf.pdf
.. _Reiner SCT cyberJack RFID komfort: http://www.reiner-sct.com/produkte/chipkartenleser/cyberJack_RFID_komfort.html
.. [#footnote1] A Comfort Reader is defined by `BSI TR-03119`_. Today the `Reiner SCT cyberJack RFID komfort`_ is the only reader that supports this configuration.

View File

@@ -1,87 +0,0 @@
.. highlight:: c
.. _npa-api:
********************************
Programming with the nPA Library
********************************
The nPA library includes a generic implementation for Secure Messaging (SM),
which might also be used in conjunction with other cards. It is implemented to
be close to ISO 7816 focusing only on encoding rather than implementing
everything that is needed to get a secure channel. All cryptographic work is
done by call back functions, which should be appropriatly set for the specific
card.
Using the German identity card (neuer Personalausweis, nPA) requires user
authentication via entry of the PIN. Transmitting the PIN in plaintext is
risky, since it would be transmitted over the air and could be snooped. That's
why the PACE keyagreement is used to verify the PIN and establish an SM channel
to the nPA. :npa:`perform_pace` does exactly that and if everything went fine,
it initializes the card for use of the SM channel. Now `sc_transmit_apdu` or
any other OpenSC command can be used to securely transmit arbitrary APDUs to
the card. Next in the Extended Access Control (EAC) are the Terminal
Authentication (:npa:`perform_terminal_authentication`) and Chip Authentication
(:npa:`perform_chip_authentication`). When the EAC was successfull, the card
will be initialized for the new SM channel. Again, libopensc can directly be
used to read identity attributes, for example.
.. note::
Please consider the following overview to the API as incomplete. The
`Doxygen documentation <../_static/doxygen-npa/modules.html>`_ should be
used as programmer's reference.
==============================================================
Interface to German identity card (neuer Personalausweis, nPA)
==============================================================
.. seealso::
`Doxygen documentation of the nPA module
<../_static/doxygen-npa/group__npa.html>`__.
---------
Functions
---------
.. doxygenfunction:: perform_pace
.. doxygenfunction:: perform_terminal_authentication
.. doxygenfunction:: perform_chip_authentication
.. doxygenfunction:: npa_reset_retry_counter
-------
Defines
-------
.. doxygendefine:: npa_change_pin
.. doxygendefine:: npa_unblock_pin
=======
Example
=======
In order to compile and execute this example you need to correctly :ref:`set up
your environment <npa-usage>`.
.. literalinclude:: example.c
:lines: 20-
======================================
Generic ISO 7816 Secure Messaging (SM)
======================================
.. seealso::
`Doxygen documentation of the SM module
<../_static/doxygen-npa/group__sm.html>`__.
-----
Types
-----
.. doxygenstruct:: iso_sm_ctx
---------
Functions
---------
.. doxygenfunction:: iso_sm_ctx_create
.. doxygenfunction:: iso_sm_ctx_clear_free
.. doxygenfunction:: iso_sm_start

View File

@@ -1,25 +0,0 @@
.. highlight:: sh
************
Installation
************
=======================================
Installation on Linux, Unix and similar
=======================================
The nPA Smart Card Library uses the GNU Build System to compile and install. If you are
unfamiliar with it, please have a look at :file:`INSTALL`. If you can not find
it, you are probably working bleeding edge in the repository. Run the
following command in :file:`npa` to get the missing standard
auxiliary files::
autoreconf --verbose --install
To configure (:command:`configure --help` lists possible options), build and
install the nPA Smart Card Library now do the following::
./configure
make
make install

View File

@@ -1 +0,0 @@
../../doc/autotools.txt.in

View File

@@ -1,14 +0,0 @@
.. highlight:: sh
********
Download
********
You can find the latest release of nPA Smart Card Library on `Github
<https://github.com/frankmorgner/vsmartcard/releases>`_. Older releases are
still available on `Sourceforge
<http://sourceforge.net/projects/vsmartcard/files>`_.
Alternatively, you can clone our git repository::
git clone https://github.com/frankmorgner/vsmartcard.git

View File

@@ -1 +0,0 @@
../../doc/download.txt.in

View File

@@ -1 +0,0 @@
../src/example.c

View File

@@ -1,6 +0,0 @@
********
Question
********
Do you have questions, suggestions or contributions? Feedback of any kind is
more than welcome! Please use our `project trackers <https://github.com/frankmorgner/vsmartcard/issues>`_.

View File

@@ -1 +0,0 @@
../../doc/questions.txt.in

View File

@@ -1,11 +0,0 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: @PACKAGE_NAME@
Description: @PACKAGE_SUMMARY@
Version: @VERSION@
Requires: libeac
Libs: -L${libdir} -lnpa @OPENSC_LIBS@
Cflags: -I${includedir}

4
npa/m4/.gitignore vendored
View File

@@ -1,4 +0,0 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@@ -1 +0,0 @@
# Process this file with automake to create Makefile.in

View File

@@ -1 +0,0 @@
../virtualsmartcard/npa-example-data

View File

@@ -1,40 +0,0 @@
app default {
# What card drivers to load at start-up
#
# A special value of 'internal' will load all
# statically linked drivers. If an unknown (ie. not
# internal) driver is supplied, a separate configuration
# configuration block has to be written for the driver.
# Default: internal
# NOTE: When "internal" keyword is used, must be last entry
#
card_drivers = npa, internal;
# Card driver configuration blocks.
#
# For card drivers loaded from an external shared library/DLL,
# you need to specify the path name of the module
#
card_driver npa {
# The location of the driver library
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;
# QES is only possible with a Comfort Reader (CAT-K), which holds a
# cryptographic key to authenticate itself as signature terminal (ST).
# We usually will use the reader's capability to sign the data.
# However, during developement you may specify soft certificates and
# keys for a ST below.
# The following example EAC PKI can be found in npa-example-data
st_dv_certificate = ZZSTDVCA00001.cvcert;
st_certificate = ZZSTTERM00001.cvcert;
st_key = ZZSTTERM00001.pkcs8;
}
}

View File

@@ -1,71 +0,0 @@
do_subst = $(SED) \
-e 's,[@]CVCDIR[@],$(CVCDIR),g' \
-e 's,[@]PACKAGE[@],$(PACKAGE),g' \
-e 's,[@]PACKAGE_BUGREPORT[@],$(PACKAGE_BUGREPORT),g' \
-e 's,[@]PACKAGE_NAME[@],$(PACKAGE_NAME),g' \
-e 's,[@]PACKAGE_TARNAME[@],$(PACKAGE_TARNAME),g' \
-e 's,[@]PACKAGE_URL[@],$(PACKAGE_URL),g' \
-e 's,[@]PACKAGE_SUMMARY[@],$(PACKAGE_SUMMARY),g' \
-e 's,[@]PACKAGE_VERSION[@],"$(PACKAGE_VERSION)",g' \
-e 's,[@]X509DIR[@],$(X509DIR),g'
BUILT_SOURCES = cmdline.h cmdline.c
EXTRA_DIST = npa-tool.ggo npa-tool.ggo.in opensc/src/common/simclist.c opensc/src/libopensc/apdu.c opensc/src/libopensc/card.c
EXTRA_DIST += $(shell find -L $(top_srcdir)/src/opensc/src -path '*/.git' -prune -o -type f -a -name '*.h' -print)
MAINTAINERCLEANFILES = $(BUILT_SOURCES) npa-tool.ggo $(dist_man1_MANS)
dist_man1_MANS = $(builddir)/npa-tool.1
libnpa_la_SOURCES = iso-sm.c scutil.c npa.c boxing.c
libnpa_la_LIBADD = $(OPENSC_LIBS) $(OPENPACE_LIBS) $(OPENSSL_LIBS)
libnpa_la_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS)
libnpa_la_LDFLAGS = -no-undefined
libcardnpa_la_SOURCES = card-npa.c
libcardnpa_la_LIBADD = $(OPENSC_LIBS) $(OPENPACE_LIBS) libnpa.la
libcardnpa_la_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS)
libcardnpa_la_LDFLAGS = -no-undefined
npa_tool_SOURCES = npa-tool.c $(BUILT_SOURCES)
npa_tool_LDADD = libnpa.la $(OPENSC_LIBS) $(OPENPACE_LIBS) $(OPENSSL_LIBS)
npa_tool_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS)
example_SOURCES = example.c
example_LDADD = $(OPENSSL_LIBS) $(OPENPACE_LIBS) $(OPENSC_LIBS) libnpa.la
example_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS)
npa-tool.c: $(BUILT_SOURCES)
$(BUILT_SOURCES): npa-tool.ggo
$(GENGETOPT) --output-dir=$(srcdir) < $<
npa-tool.ggo: npa-tool.ggo.in
$(do_subst) < $< > $(srcdir)/$@
npa-tool.1: npa-tool.ggo.in
$(HELP2MAN) \
--output=$@ \
--no-info \
--source='$(PACKAGE_STRING)' \
$(builddir)/npa-tool
bin_PROGRAMS = npa-tool
noinst_PROGRAMS = example
lib_LTLIBRARIES = libnpa.la libcardnpa.la
noinst_HEADERS = \
sslutil.h \
card-npa.h \
iso-sm-internal.h \
ccid-types.h
nobase_include_HEADERS = \
npa/iso-sm.h \
npa/npa.h \
npa/boxing.h \
npa/scutil.h

View File

@@ -1,996 +0,0 @@
/*
* boxing.c: implementation related to boxing commands with pseudo APDUs
*
* Copyright (C) 2013 Frank Morgner <frankmorgner@gmail.com>
*
* This file is part of libnpa.
*
* libnpa is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libnpa 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* libnpa. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ccid-types.h"
#include <libopensc/asn1.h>
#include <libopensc/log.h>
#include <libopensc/opensc.h>
#include <libopensc/pace.h>
#include <npa/boxing.h>
#include <npa/scutil.h>
#include <stdlib.h>
#include <string.h>
#if _WIN32
/* FIXME might not always work */
#define htole16(x) (x)
#define htole32(x) (x)
#elif __APPLE__
#include <libkern/OSByteOrder.h>
#define htole16(x) OSSwapHostToLittleInt16(x)
#define htole32(x) OSSwapHostToLittleInt32(x)
#else
#ifndef _BSD_SOURCE
#define _BSD_SOURCE /* See feature_test_macros(7) */
#endif
#include <endian.h>
#endif
static const u8 boxing_cla = 0xff;
static const u8 boxing_ins = 0x9a;
static const u8 boxing_p1 = 0x04;
static const u8 boxing_p2_GetReaderPACECapabilities = 0x01;
static const u8 boxing_p2_EstablishPACEChannel = 0x02;
static const u8 boxing_p2_DestroyPACEChannel = 0x03;
static const u8 boxing_p2_PC_to_RDR_Secure = 0x10;
struct sc_asn1_entry g_boolean[] = {
{ "boolean",
SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, 0, NULL, NULL },
{ NULL , 0 , 0 , 0 , NULL , NULL }
};
struct sc_asn1_entry g_int_as_octet_string[] = {
{ "int as octet string",
SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, 0, NULL, NULL },
{ NULL , 0 , 0 , 0 , NULL , NULL }
};
struct sc_asn1_entry g_octet_string[] = {
{ "octet string",
SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_ALLOC, NULL, NULL },
{ NULL , 0 , 0 , 0 , NULL , NULL }
};
struct sc_asn1_entry g_numeric_string_as_octet_string[] = {
{ "utf8string",
SC_ASN1_OCTET_STRING, SC_ASN1_TAG_NUMERICSTRING, SC_ASN1_ALLOC, NULL, NULL },
{ NULL , 0 , 0 , 0 , NULL , NULL }
};
static const struct sc_asn1_entry g_EstablishPACEChannelInput_data[] = {
{ "passwordID",
/* use an OCTET STRING to avoid a conversion to int */
SC_ASN1_STRUCT, SC_ASN1_CTX|0x01|SC_ASN1_CONS, 0, NULL, NULL },
{ "transmittedPassword",
SC_ASN1_STRUCT, SC_ASN1_CTX|0x02|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
{ "cHAT",
SC_ASN1_STRUCT, SC_ASN1_CTX|0x03|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
{ "certificateDescription",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x04|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
{ "hashOID",
/* use an OCTET STRING to avoid a conversion to struct sc_object_id */
SC_ASN1_STRUCT, SC_ASN1_CTX|0x05|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
{ NULL , 0 , 0 , 0 , NULL , NULL }
};
static const struct sc_asn1_entry g_EstablishPACEChannelOutput_data[] = {
{ "errorCode",
SC_ASN1_STRUCT, SC_ASN1_CTX|0x01|SC_ASN1_CONS, 0, NULL, NULL },
{ "statusMSESetAT",
SC_ASN1_STRUCT, SC_ASN1_CTX|0x02|SC_ASN1_CONS, 0, NULL, NULL },
{ "efCardAccess",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x03|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL },
{ "idPICC",
SC_ASN1_STRUCT, SC_ASN1_CTX|0x04|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
{ "curCAR",
SC_ASN1_STRUCT, SC_ASN1_CTX|0x05|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
{ "prevCAR",
SC_ASN1_STRUCT, SC_ASN1_CTX|0x06|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
{ NULL , 0 , 0 , 0 , NULL , NULL }
};
static const struct sc_asn1_entry g_EstablishPACEChannel[] = {
{ "EstablishPACEChannel",
SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE|SC_ASN1_CONS, 0, NULL, NULL },
{ NULL , 0 , 0 , 0 , NULL , NULL }
};
int boxing_pace_input_to_buf(sc_context_t *ctx,
const struct establish_pace_channel_input *input,
unsigned char **asn1, size_t *asn1_len)
{
size_t pin_id_len = sizeof input->pin_id;
struct sc_asn1_entry EstablishPACEChannelInput_data[
sizeof g_EstablishPACEChannelInput_data/
sizeof *g_EstablishPACEChannelInput_data];
struct sc_asn1_entry EstablishPACEChannel[
sizeof g_EstablishPACEChannel/
sizeof *g_EstablishPACEChannel];
struct sc_asn1_entry passwordID[
sizeof g_int_as_octet_string/
sizeof *g_int_as_octet_string];
struct sc_asn1_entry transmittedPassword[
sizeof g_numeric_string_as_octet_string/
sizeof *g_numeric_string_as_octet_string];
struct sc_asn1_entry cHAT[
sizeof g_octet_string/
sizeof *g_octet_string];
sc_copy_asn1_entry(g_EstablishPACEChannel,
EstablishPACEChannel);
sc_format_asn1_entry(EstablishPACEChannel,
EstablishPACEChannelInput_data, 0, 1);
sc_copy_asn1_entry(g_EstablishPACEChannelInput_data,
EstablishPACEChannelInput_data);
sc_format_asn1_entry(EstablishPACEChannelInput_data+0,
passwordID, 0, 1);
sc_copy_asn1_entry(g_int_as_octet_string,
passwordID);
sc_format_asn1_entry(passwordID,
(unsigned char *) &input->pin_id, &pin_id_len, 1);
if (input->pin) {
sc_format_asn1_entry(EstablishPACEChannelInput_data+1,
transmittedPassword,
0, 1);
sc_copy_asn1_entry(g_numeric_string_as_octet_string,
transmittedPassword);
sc_format_asn1_entry(transmittedPassword,
(unsigned char *) input->pin,
(size_t *) &input->pin_length, 1);
}
if (input->chat) {
sc_format_asn1_entry(EstablishPACEChannelInput_data+2,
cHAT,
0, 1);
sc_copy_asn1_entry(g_octet_string,
cHAT);
sc_format_asn1_entry(cHAT,
(unsigned char *) input->chat,
(size_t *) &input->chat_length, 1);
}
if (input->certificate_description) {
sc_format_asn1_entry(EstablishPACEChannelInput_data+3,
(unsigned char *) input->certificate_description,
(size_t *) &input->certificate_description_length, 1);
}
return sc_asn1_encode(ctx, EstablishPACEChannel, asn1, asn1_len);
}
int boxing_buf_to_pace_input(sc_context_t *ctx,
const unsigned char *asn1, size_t asn1_len,
struct establish_pace_channel_input *input)
{
size_t pin_id_len = sizeof input->pin_id;
struct sc_asn1_entry EstablishPACEChannelInput_data[
sizeof g_EstablishPACEChannelInput_data/
sizeof *g_EstablishPACEChannelInput_data];
struct sc_asn1_entry EstablishPACEChannel[
sizeof g_EstablishPACEChannel/
sizeof *g_EstablishPACEChannel];
struct sc_asn1_entry passwordID[
sizeof g_int_as_octet_string/
sizeof *g_int_as_octet_string];
struct sc_asn1_entry transmittedPassword[
sizeof g_numeric_string_as_octet_string/
sizeof *g_numeric_string_as_octet_string];
struct sc_asn1_entry cHAT[
sizeof g_octet_string/
sizeof *g_octet_string];
/* FIXME handle hashOID */
sc_copy_asn1_entry(g_EstablishPACEChannel,
EstablishPACEChannel);
sc_format_asn1_entry(EstablishPACEChannel,
EstablishPACEChannelInput_data, 0, 0);
sc_copy_asn1_entry(g_EstablishPACEChannelInput_data,
EstablishPACEChannelInput_data);
sc_format_asn1_entry(EstablishPACEChannelInput_data+0,
passwordID, 0, 0);
sc_copy_asn1_entry(g_int_as_octet_string,
passwordID);
sc_format_asn1_entry(passwordID,
&input->pin_id, &pin_id_len, 0);
if (input->pin) {
sc_format_asn1_entry(EstablishPACEChannelInput_data+1,
transmittedPassword, 0, 0);
sc_copy_asn1_entry(g_numeric_string_as_octet_string,
transmittedPassword);
sc_format_asn1_entry(transmittedPassword,
(unsigned char *) &input->pin, &input->pin_length, 0);
}
if (input->chat) {
sc_format_asn1_entry(EstablishPACEChannelInput_data+2,
cHAT, 0, 0);
sc_copy_asn1_entry(g_octet_string,
cHAT);
sc_format_asn1_entry(cHAT,
(unsigned char *) &input->chat, &input->chat_length, 0);
}
if (input->certificate_description) {
sc_format_asn1_entry(EstablishPACEChannelInput_data+3,
(unsigned char *) &input->certificate_description,
&input->certificate_description_length, 0);
}
LOG_TEST_RET(ctx,
sc_asn1_decode(ctx, EstablishPACEChannel, asn1, asn1_len, NULL, NULL),
"Error decoding EstablishPACEChannel");
if (pin_id_len != sizeof input->pin_id)
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
return SC_SUCCESS;
}
int boxing_pace_output_to_buf(sc_context_t *ctx,
const struct establish_pace_channel_output *output,
unsigned char **asn1, size_t *asn1_len)
{
uint16_t status_mse_set_at = ((output->mse_set_at_sw1 & 0xff) << 8) | output->mse_set_at_sw2;
size_t result_len = sizeof output->result,
status_mse_set_at_len = sizeof status_mse_set_at;
struct sc_asn1_entry EstablishPACEChannelOutput_data[
sizeof g_EstablishPACEChannelOutput_data/
sizeof *g_EstablishPACEChannelOutput_data];
struct sc_asn1_entry EstablishPACEChannel[
sizeof g_EstablishPACEChannel/
sizeof *g_EstablishPACEChannel];
struct sc_asn1_entry errorCode[
sizeof g_octet_string/
sizeof *g_octet_string];
struct sc_asn1_entry statusMSESetAT[
sizeof g_octet_string/
sizeof *g_octet_string];
struct sc_asn1_entry idPICC[
sizeof g_octet_string/
sizeof *g_octet_string];
struct sc_asn1_entry curCAR[
sizeof g_octet_string/
sizeof *g_octet_string];
struct sc_asn1_entry prevCAR[
sizeof g_octet_string/
sizeof *g_octet_string];
sc_copy_asn1_entry(g_EstablishPACEChannel,
EstablishPACEChannel);
sc_format_asn1_entry(EstablishPACEChannel,
EstablishPACEChannelOutput_data, 0, 1);
sc_copy_asn1_entry(g_EstablishPACEChannelOutput_data,
EstablishPACEChannelOutput_data);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+0,
errorCode, 0, 1);
sc_copy_asn1_entry(g_octet_string,
errorCode);
sc_format_asn1_entry(errorCode,
(unsigned char *) &output->result, &result_len, 1);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+1,
statusMSESetAT, 0, 1);
sc_copy_asn1_entry(g_octet_string,
statusMSESetAT);
sc_format_asn1_entry(statusMSESetAT,
&status_mse_set_at, &status_mse_set_at_len, 1);
if (output->ef_cardaccess) {
sc_format_asn1_entry(EstablishPACEChannelOutput_data+2,
output->ef_cardaccess, (size_t *) &output->ef_cardaccess_length, 1);
}
if (output->id_icc) {
sc_format_asn1_entry(EstablishPACEChannelOutput_data+3,
idPICC, 0, 1);
sc_copy_asn1_entry(g_octet_string,
idPICC);
sc_format_asn1_entry(idPICC,
output->id_icc, (size_t *) &output->id_icc_length, 1);
}
if (output->recent_car) {
sc_format_asn1_entry(EstablishPACEChannelOutput_data+4,
curCAR, 0, 1);
sc_copy_asn1_entry(g_octet_string,
curCAR);
sc_format_asn1_entry(curCAR,
output->recent_car, (size_t *) &output->recent_car_length, 1);
}
if (output->previous_car) {
sc_format_asn1_entry(EstablishPACEChannelOutput_data+5,
prevCAR, 0, 1);
sc_copy_asn1_entry(g_octet_string,
prevCAR);
sc_format_asn1_entry(prevCAR,
output->previous_car, (size_t *) &output->previous_car_length, 1);
}
return sc_asn1_encode(ctx, EstablishPACEChannel, asn1, asn1_len);
}
int boxing_buf_to_pace_output(sc_context_t *ctx,
const unsigned char *asn1, size_t asn1_len,
struct establish_pace_channel_output *output)
{
uint16_t status_mse_set_at;
size_t result_len = sizeof output->result,
status_mse_set_at_len = sizeof status_mse_set_at;
struct sc_asn1_entry EstablishPACEChannelOutput_data[
sizeof g_EstablishPACEChannelOutput_data/
sizeof *g_EstablishPACEChannelOutput_data];
struct sc_asn1_entry EstablishPACEChannel[
sizeof g_EstablishPACEChannel/
sizeof *g_EstablishPACEChannel];
struct sc_asn1_entry errorCode[
sizeof g_octet_string/
sizeof *g_octet_string];
struct sc_asn1_entry statusMSESetAT[
sizeof g_octet_string/
sizeof *g_octet_string];
struct sc_asn1_entry idPICC[
sizeof g_octet_string/
sizeof *g_octet_string];
struct sc_asn1_entry curCAR[
sizeof g_octet_string/
sizeof *g_octet_string];
struct sc_asn1_entry prevCAR[
sizeof g_octet_string/
sizeof *g_octet_string];
sc_copy_asn1_entry(g_EstablishPACEChannel,
EstablishPACEChannel);
sc_format_asn1_entry(EstablishPACEChannel,
EstablishPACEChannelOutput_data, 0, 0);
sc_copy_asn1_entry(g_EstablishPACEChannelOutput_data,
EstablishPACEChannelOutput_data);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+0,
errorCode, 0, 0);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+1,
statusMSESetAT, 0, 0);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+2,
&output->ef_cardaccess, &output->ef_cardaccess_length, 0);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+3,
idPICC, 0, 0);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+4,
curCAR, 0, 0);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+5,
prevCAR, 0, 0);
sc_copy_asn1_entry(g_octet_string,
errorCode);
sc_format_asn1_entry(errorCode,
&output->result, &result_len, 0);
/* we already allocated memory for the result */
errorCode->flags = 0;
sc_copy_asn1_entry(g_octet_string,
statusMSESetAT);
sc_format_asn1_entry(statusMSESetAT,
&status_mse_set_at, &status_mse_set_at_len, 0);
/* we already allocated memory for the result */
statusMSESetAT->flags = 0;
sc_copy_asn1_entry(g_octet_string,
idPICC);
sc_format_asn1_entry(idPICC,
&output->id_icc, &output->id_icc_length, 0);
sc_copy_asn1_entry(g_octet_string,
curCAR);
sc_format_asn1_entry(curCAR,
&output->recent_car, &output->recent_car_length, 0);
sc_copy_asn1_entry(g_octet_string,
prevCAR);
sc_format_asn1_entry(prevCAR,
&output->previous_car, &output->previous_car_length, 0);
LOG_TEST_RET(ctx,
sc_asn1_decode(ctx, EstablishPACEChannel,
asn1, asn1_len, NULL, NULL),
"Error decoding EstablishPACEChannel");
if (status_mse_set_at_len != sizeof status_mse_set_at
|| result_len != sizeof output->result)
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
output->mse_set_at_sw1 = (status_mse_set_at >> 8) & 0xff;
output->mse_set_at_sw2 = status_mse_set_at & 0xff;
return SC_SUCCESS;
}
#define CCID_PIN_TIMEOUT 30
#define CCID_DISPLAY_DEFAULT 0xff
static int boxing_pin_cmd_to_buf(sc_context_t *ctx,
const struct sc_pin_cmd_data *data,
unsigned char **pc_to_rdr_secure, size_t *pc_to_rdr_secure_len)
{
PC_to_RDR_Secure_t *secure;
abPINDataStucture_Modification_t *modify;
abPINDataStucture_Verification_t *verify;
uint16_t wLangId = 0,
bTeoPrologue2 = 0,
wPINMaxExtraDigit;
uint8_t bTimeOut = CCID_PIN_TIMEOUT,
bNumberMessage = CCID_DISPLAY_DEFAULT,
bTeoPrologue1 = 0,
bMsgIndex = 0,
bMessageType = 0x69,
bSlot = 0,
bSeq = 0,
bBWI = 0xff,
wLevelParameter = 0,
bEntryValidationCondition = CCID_ENTRY_VALIDATE,
bmFormatString, bmPINLengthFormat, bmPINBlockString;
const struct sc_pin_cmd_pin *pin_ref;
int r;
unsigned char *pinapdu = NULL;
size_t pinapdu_len = 0;
if (!data || !pc_to_rdr_secure || !pc_to_rdr_secure_len) {
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
pin_ref = data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ?
&data->pin2 : &data->pin1;
wPINMaxExtraDigit = htole16(
(0xff & pin_ref->min_length) << 8)
| (pin_ref->max_length & 0xff);
bmFormatString = CCID_PIN_UNITS_BYTES
| ((pin_ref->offset & 0xf) << 3);
switch (pin_ref->encoding) {
case SC_PIN_ENCODING_ASCII:
bmFormatString |= CCID_PIN_ENCODING_ASCII;
break;
case SC_PIN_ENCODING_BCD:
bmFormatString |= CCID_PIN_ENCODING_BCD;
break;
default:
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
/* GLP PINs expect the effective PIN length from bit 4 */
bmPINLengthFormat = pin_ref->encoding == SC_PIN_ENCODING_GLP ?
0x04 : 0x00;
if (pin_ref->encoding == SC_PIN_ENCODING_GLP) {
/* GLP PIN length is encoded in 4 bits and block size is always 8 bytes */
bmPINBlockString = 0x40 | 0x08;
} else if (pin_ref->encoding == SC_PIN_ENCODING_ASCII && data->flags & SC_PIN_CMD_NEED_PADDING) {
bmPINBlockString = pin_ref->pad_length;
} else {
bmPINBlockString = 0x00;
}
r = sc_apdu_get_octets(ctx, data->apdu, &pinapdu, &pinapdu_len,
SC_PROTO_T1);
if (r < 0)
goto err;
switch (data->cmd) {
case SC_PIN_CMD_VERIFY:
*pc_to_rdr_secure_len = sizeof *secure + 1
+ sizeof *verify + pinapdu_len;
break;
case SC_PIN_CMD_CHANGE:
*pc_to_rdr_secure_len = sizeof *secure + 1
+ sizeof *modify + 3 + pinapdu_len;
break;
default:
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
*pc_to_rdr_secure = malloc(*pc_to_rdr_secure_len);
if (!*pc_to_rdr_secure) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
secure = (PC_to_RDR_Secure_t *) *pc_to_rdr_secure;
secure->bMessageType = bMessageType;
secure->dwLength = htole32((*pc_to_rdr_secure_len) - sizeof *secure);
secure->bSlot = bSlot;
secure->bSeq = bSeq;
secure->bBWI = bBWI;
secure->wLevelParameter = wLevelParameter;
switch (data->cmd) {
case SC_PIN_CMD_VERIFY:
/* bPINOperation */
*((*pc_to_rdr_secure) + sizeof *secure) = CCID_OPERATION_VERIFY;
verify = (abPINDataStucture_Verification_t *)
((*pc_to_rdr_secure) + sizeof *secure + 1);
verify->bTimeOut = bTimeOut;
verify->bmFormatString = bmFormatString;
verify->bmPINBlockString = bmPINBlockString;
verify->bmPINLengthFormat = bmPINLengthFormat;
verify->wPINMaxExtraDigit = wPINMaxExtraDigit;
verify->bEntryValidationCondition = bEntryValidationCondition;
verify->bNumberMessage = bNumberMessage;
verify->wLangId = wLangId;
verify->bMsgIndex = bMsgIndex;
verify->bTeoPrologue1 = bTeoPrologue1;
verify->bTeoPrologue2 = bTeoPrologue2;
memcpy((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *verify,
pinapdu, pinapdu_len);
break;
case SC_PIN_CMD_CHANGE:
/* bPINOperation */
*((*pc_to_rdr_secure) + sizeof *secure) = CCID_OPERATION_MODIFY;
modify = (abPINDataStucture_Modification_t *)
((*pc_to_rdr_secure) + sizeof *secure + 1);
modify->bTimeOut = bTimeOut;
modify->bmFormatString = bmFormatString;
modify->bmPINBlockString = bmPINBlockString;
modify->bmPINLengthFormat = bmPINLengthFormat;
if (!(data->flags & SC_PIN_CMD_IMPLICIT_CHANGE)
&& data->pin1.offset) {
modify->bInsertionOffsetOld = data->pin1.offset - 5;
} else {
modify->bInsertionOffsetOld = 0;
}
modify->bInsertionOffsetNew = data->pin2.offset ? data->pin2.offset - 5 : 0;
modify->wPINMaxExtraDigit = wPINMaxExtraDigit;
modify->bConfirmPIN = CCID_PIN_CONFIRM_NEW
| (data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ? 0 : CCID_PIN_INSERT_OLD);
modify->bEntryValidationCondition = bEntryValidationCondition;
modify->bNumberMessage = bNumberMessage;
modify->wLangId = wLangId;
modify->bMsgIndex1 = bMsgIndex;
*((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 0) =
bTeoPrologue1;
*((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 1) =
bTeoPrologue1;
*((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 2) =
bTeoPrologue1;
memcpy((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 3,
pinapdu, pinapdu_len);
break;
default:
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
r = SC_SUCCESS;
err:
free(pinapdu);
if (r < 0 && pc_to_rdr_secure && *pc_to_rdr_secure) {
free(*pc_to_rdr_secure);
*pc_to_rdr_secure = NULL;
}
return r;
}
#define CCID_BSTATUS_OK_ACTIVE 0x00 /** No error. An ICC is present and active */
static int boxing_buf_to_verify_result(sc_context_t *ctx,
const unsigned char *rdr_to_pc_datablock,
size_t rdr_to_pc_datablock_len,
sc_apdu_t *apdu)
{
RDR_to_PC_DataBlock_t *datablock =
(RDR_to_PC_DataBlock_t *) rdr_to_pc_datablock;
if (!rdr_to_pc_datablock
|| rdr_to_pc_datablock_len < sizeof *datablock
|| datablock->bMessageType != 0x80)
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
if (datablock->bStatus != CCID_BSTATUS_OK_ACTIVE)
return SC_ERROR_TRANSMIT_FAILED;
return sc_apdu_set_resp(ctx, apdu,
rdr_to_pc_datablock + sizeof *datablock,
htole32(datablock->dwLength));
}
static int boxing_perform_verify(struct sc_reader *reader,
struct sc_pin_cmd_data *data)
{
u8 rbuf[0xff];
sc_apdu_t apdu;
int r;
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_4_SHORT;
apdu.cla = boxing_cla;
apdu.ins = boxing_ins;
apdu.p1 = boxing_p1;
apdu.p2 = boxing_p2_PC_to_RDR_Secure;
apdu.resp = rbuf;
apdu.resplen = sizeof rbuf;
apdu.le = sizeof rbuf;
if (!reader || !reader->ops || !reader->ops->transmit) {
r = SC_ERROR_NOT_SUPPORTED;
goto err;
}
r = boxing_pin_cmd_to_buf(reader->ctx, data,
(unsigned char **) &apdu.data, &apdu.datalen);
if (r < 0) {
sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
"Error encoding PC_to_RDR_Secure");
goto err;
}
apdu.lc = apdu.datalen;
r = SC_SUCCESS;
r = reader->ops->transmit(reader, &apdu);
if (r < 0) {
sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
"Error performing PC_to_RDR_Secure");
goto err;
}
if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) {
sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
"Error decoding PC_to_RDR_Secure");
r = SC_ERROR_NOT_SUPPORTED;
goto err;
}
r = boxing_buf_to_verify_result(reader->ctx, apdu.resp, apdu.resplen,
data->apdu);
err:
free((unsigned char *) apdu.data);
return r;
}
static int boxing_perform_pace(struct sc_reader *reader,
void *establish_pace_channel_input,
void *establish_pace_channel_output)
{
u8 rbuf[0xffff];
sc_apdu_t apdu;
int r;
struct establish_pace_channel_input *input =
establish_pace_channel_input;
struct establish_pace_channel_output *output =
establish_pace_channel_output;
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_4_EXT;
apdu.cla = boxing_cla;
apdu.ins = boxing_ins;
apdu.p1 = boxing_p1;
apdu.p2 = boxing_p2_EstablishPACEChannel;
apdu.resp = rbuf;
apdu.resplen = sizeof rbuf;
apdu.le = sizeof rbuf;
if (!reader || !reader->ops || !reader->ops->transmit) {
r = SC_ERROR_NOT_SUPPORTED;
goto err;
}
r = boxing_pace_input_to_buf(reader->ctx, input,
(unsigned char **) &apdu.data, &apdu.datalen);
if (r < 0) {
sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
"Error encoding EstablishPACEChannel");
goto err;
}
apdu.lc = apdu.datalen;
r = reader->ops->transmit(reader, &apdu);
if (r < 0) {
sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
"Error performing EstablishPACEChannel");
goto err;
}
if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) {
sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
"Error decoding EstablishPACEChannel");
r = SC_ERROR_NOT_SUPPORTED;
goto err;
}
r = boxing_buf_to_pace_output(reader->ctx, apdu.resp, apdu.resplen,
output);
err:
free((unsigned char *) apdu.data);
return r;
}
struct sc_asn1_entry g_PACECapabilities_data[] = {
{ "capabilityPACE",
SC_ASN1_STRUCT, SC_ASN1_CTX|0x01|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL },
{ "capabilityEID",
SC_ASN1_STRUCT, SC_ASN1_CTX|0x02|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL },
{ "capabilityESign",
SC_ASN1_STRUCT, SC_ASN1_CTX|0x03|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL },
{ "capabilityDestroy",
SC_ASN1_STRUCT, SC_ASN1_CTX|0x04|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL },
{ NULL , 0 , 0 , 0 , NULL , NULL }
};
struct sc_asn1_entry g_PACECapabilities[] = {
{ "PACECapabilities",
SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE|SC_ASN1_CONS, 0, NULL, NULL },
{ NULL , 0 , 0 , 0 , NULL , NULL }
};
int boxing_buf_to_pace_capabilities(sc_context_t *ctx,
const unsigned char *asn1, size_t asn1_len,
unsigned long *sc_reader_t_capabilities)
{
int pace = 0, eid = 0, esign = 0, destroy = 0;
struct sc_asn1_entry PACECapabilities_data[
sizeof g_PACECapabilities_data/
sizeof *g_PACECapabilities_data];
struct sc_asn1_entry PACECapabilities[
sizeof g_PACECapabilities/
sizeof *g_PACECapabilities];
struct sc_asn1_entry capabilityPACE[
sizeof g_boolean/
sizeof *g_boolean];
struct sc_asn1_entry capabilityEID[
sizeof g_boolean/
sizeof *g_boolean];
struct sc_asn1_entry capabilityESign[
sizeof g_boolean/
sizeof *g_boolean];
struct sc_asn1_entry capabilityDestroy[
sizeof g_boolean/
sizeof *g_boolean];
sc_copy_asn1_entry(g_PACECapabilities,
PACECapabilities);
sc_format_asn1_entry(PACECapabilities,
PACECapabilities_data, 0, 1);
sc_copy_asn1_entry(g_PACECapabilities_data,
PACECapabilities_data);
sc_format_asn1_entry(PACECapabilities_data+0,
&capabilityPACE, NULL, 1);
sc_format_asn1_entry(PACECapabilities_data+1,
&capabilityEID, NULL, 1);
sc_format_asn1_entry(PACECapabilities_data+2,
&capabilityESign, NULL, 1);
sc_format_asn1_entry(PACECapabilities_data+3,
&capabilityDestroy, NULL, 1);
sc_copy_asn1_entry(g_boolean,
capabilityPACE);
sc_format_asn1_entry(capabilityPACE+0,
&pace, NULL, 0);
sc_copy_asn1_entry(g_boolean,
capabilityEID);
sc_format_asn1_entry(capabilityEID+0,
&eid, NULL, 0);
sc_copy_asn1_entry(g_boolean,
capabilityESign);
sc_format_asn1_entry(capabilityESign+0,
&esign, NULL, 0);
sc_copy_asn1_entry(g_boolean,
capabilityDestroy);
sc_format_asn1_entry(capabilityDestroy+0,
&destroy, NULL, 0);
LOG_TEST_RET(ctx,
sc_asn1_decode(ctx, PACECapabilities,
asn1, asn1_len, NULL, NULL),
"Error decoding PACECapabilities");
/* We got a valid PACE Capabilities reply. There is currently no mechanism
* to determine support PIN verification/modification with a boxing
* command. Since the reader implements this mechanism it is reasonable to
* assume that PIN verification/modification is available. */
*sc_reader_t_capabilities = SC_READER_CAP_PIN_PAD;
if (pace)
*sc_reader_t_capabilities |= SC_READER_CAP_PACE_GENERIC;
if (eid)
*sc_reader_t_capabilities |= SC_READER_CAP_PACE_EID;
if (esign)
*sc_reader_t_capabilities |= SC_READER_CAP_PACE_ESIGN;
if (destroy)
*sc_reader_t_capabilities |= SC_READER_CAP_PACE_DESTROY_CHANNEL;
return SC_SUCCESS;
}
int boxing_pace_capabilities_to_buf(sc_context_t *ctx,
const unsigned long sc_reader_t_capabilities,
unsigned char **asn1, size_t *asn1_len)
{
int yes = 1, no = 0;
struct sc_asn1_entry PACECapabilities_data[
sizeof g_PACECapabilities_data/
sizeof *g_PACECapabilities_data];
struct sc_asn1_entry PACECapabilities[
sizeof g_PACECapabilities/
sizeof *g_PACECapabilities];
struct sc_asn1_entry capabilityPACE[
sizeof g_boolean/
sizeof *g_boolean];
struct sc_asn1_entry capabilityEID[
sizeof g_boolean/
sizeof *g_boolean];
struct sc_asn1_entry capabilityESign[
sizeof g_boolean/
sizeof *g_boolean];
struct sc_asn1_entry capabilityDestroy[
sizeof g_boolean/
sizeof *g_boolean];
sc_copy_asn1_entry(g_EstablishPACEChannel,
PACECapabilities);
sc_format_asn1_entry(PACECapabilities,
PACECapabilities_data, 0, 1);
sc_copy_asn1_entry(g_PACECapabilities_data,
PACECapabilities_data);
sc_format_asn1_entry(PACECapabilities_data+0,
&capabilityPACE, NULL, 1);
sc_format_asn1_entry(PACECapabilities_data+1,
&capabilityEID, NULL, 1);
sc_format_asn1_entry(PACECapabilities_data+2,
&capabilityESign, NULL, 1);
sc_format_asn1_entry(PACECapabilities_data+3,
&capabilityDestroy, NULL, 1);
sc_copy_asn1_entry(g_boolean,
capabilityPACE);
sc_format_asn1_entry(capabilityPACE,
sc_reader_t_capabilities & SC_READER_CAP_PACE_GENERIC
? &yes : &no, NULL, 1);
sc_copy_asn1_entry(g_boolean,
capabilityEID);
sc_format_asn1_entry(capabilityEID,
sc_reader_t_capabilities & SC_READER_CAP_PACE_EID
? &yes : &no, NULL, 1);
sc_copy_asn1_entry(g_boolean,
capabilityESign);
sc_format_asn1_entry(capabilityESign,
sc_reader_t_capabilities & SC_READER_CAP_PACE_ESIGN
? &yes : &no, NULL, 1);
sc_copy_asn1_entry(g_boolean,
capabilityDestroy);
sc_format_asn1_entry(capabilityDestroy,
sc_reader_t_capabilities & SC_READER_CAP_PACE_DESTROY_CHANNEL
? &yes : &no, NULL, 1);
return sc_asn1_encode(ctx, PACECapabilities, asn1, asn1_len);
}
#ifndef DISABLE_GLOBAL_BOXING_INITIALIZATION
static
#endif
void sc_detect_boxing_cmds(sc_reader_t *reader)
{
u8 rbuf[0xff];
sc_apdu_t apdu;
unsigned long capabilities;
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_1;
apdu.cla = boxing_cla;
apdu.ins = boxing_ins;
apdu.p1 = boxing_p1;
apdu.p2 = boxing_p2_GetReaderPACECapabilities;
apdu.resp = rbuf;
apdu.resplen = sizeof rbuf;
apdu.le = sizeof rbuf;
if (!reader || !reader->ops || !reader->ops->transmit
|| reader->ops->transmit(reader, &apdu) != SC_SUCCESS
|| apdu.sw1 != 0x90
|| apdu.sw2 != 0x00
|| boxing_buf_to_pace_capabilities(reader->ctx,
apdu.resp, apdu.resplen, &capabilities) != SC_SUCCESS) {
if (reader) {
sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
"%s does not support boxing commands", reader->name);
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_3;
apdu.cla = 0x00;
apdu.ins = 0xA4;
apdu.p1 = 8;
apdu.p2 = 0x0C;
apdu.data = rbuf;
rbuf[0] = 0x3F;
rbuf[1] = 0x00;
apdu.datalen = 2;
apdu.lc = 2;
apdu.resp = NULL;
apdu.resplen = 0;
apdu.le = 0;
reader->ops->transmit(reader, &apdu);
}
} else {
if (capabilities & SC_READER_CAP_PIN_PAD
&& !(reader->capabilities & SC_READER_CAP_PIN_PAD)) {
((struct sc_reader_operations *) reader->ops)->perform_verify =
boxing_perform_verify;
sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
"Added boxing command wrappers for PIN verification/modification to '%s'", reader->name);
}
if (capabilities & SC_READER_CAP_PACE_GENERIC
&& !(reader->capabilities & SC_READER_CAP_PACE_GENERIC)) {
((struct sc_reader_operations *) reader->ops)->perform_pace =
boxing_perform_pace;
sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
"Added boxing command wrappers for PACE to '%s'", reader->name);
}
reader->capabilities |= capabilities;
}
}
#ifndef DISABLE_GLOBAL_BOXING_INITIALIZATION
#ifndef HAVE_LIST_ITERATOR_NEXT
#include "opensc/src/common/simclist.c"
#endif
void sc_initialize_boxing_cmds(sc_context_t *ctx)
{
sc_reader_t *reader;
if (!ctx)
return;
if (!list_iterator_start(&ctx->readers))
return;
reader = list_iterator_next(&ctx->readers);
while (reader) {
reader = list_iterator_next(&ctx->readers);
sc_detect_boxing_cmds(reader);
}
list_iterator_stop(&ctx->readers);
}
#endif

View File

@@ -1,704 +0,0 @@
/*
* card-npa.c: Recognize known German identity cards
*
* Copyright (C) 2011-2014 Frank Morgner <frankmorgner@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "iso-sm-internal.h"
#include "libopensc/internal.h"
#include "libopensc/pace.h"
#include "libopensc/sm.h"
#include "card-npa.h"
#include <npa/boxing.h>
#include <npa/npa.h>
#include <npa/scutil.h>
#include <string.h>
struct npa_drv_data {
const char *can;
unsigned char *st_dv_certificate;
size_t st_dv_certificate_len;
unsigned char *st_certificate;
size_t st_certificate_len;
unsigned char *st_key;
size_t st_key_len;
unsigned char *ef_cardaccess;
size_t ef_cardaccess_length;
unsigned char *ef_cardsecurity;
size_t ef_cardsecurity_length;
};
static struct npa_drv_data *npa_drv_data_create(void)
{
struct npa_drv_data *drv_data = calloc(1, sizeof *drv_data);
return drv_data;
}
static void npa_drv_data_free(struct npa_drv_data *drv_data)
{
if (drv_data) {
free(drv_data->ef_cardaccess);
free(drv_data->ef_cardsecurity);
free(drv_data->st_certificate);
free(drv_data->st_dv_certificate);
free(drv_data->st_key);
free(drv_data);
}
}
static struct sc_atr_table npa_atrs[] = {
{"3B:8A:80:01:80:31:F8:73:F7:41:E0:82:90:00:75",
"FF:FF:FF:FF:FF:FF:00:FF:00:00:FF:FF:FF:FF:00",
"German ID card (neuer Personalausweis, nPA)", SC_CARD_TYPE_NPA, 0, NULL},
{"3B:88:80:01:00:00:00:00:00:00:00:00:09", NULL,
"German ID card (neuer Personalausweis, nPA)", SC_CARD_TYPE_NPA, 0, NULL},
{"3B:87:80:01:80:31:B8:73:84:01:E0:19", NULL,
"German ID card (neuer Personalausweis, nPA)", SC_CARD_TYPE_NPA, 0, NULL},
{"3B:84:80:01:00:00:90:00:95", NULL,
"German ID card (Test neuer Personalausweis)", SC_CARD_TYPE_NPA_TEST, 0, NULL},
{"3B:88:80:01:00:E1:F3:5E:13:77:83:00:00",
"FF:FF:FF:FF:00:FF:FF:FF:FF:FF:FF:FF:00",
"German ID card (Test Online-Ausweisfunktion)", SC_CARD_TYPE_NPA_ONLINE, 0, NULL},
{NULL, NULL, NULL, 0, 0, NULL}
};
static struct sc_card_operations npa_ops;
static struct sc_card_driver npa_drv = {
"German ID card (neuer Personalausweis, nPA)",
"npa",
&npa_ops,
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 || !ctx->conf_blocks) {
r = SC_ERROR_INTERNAL;
goto err;
}
for (i = 0; ctx->conf_blocks[i]; i++) {
found_blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i],
"card_driver", "npa");
if (!found_blocks)
continue;
for (j = 0, block = found_blocks[j]; block; j++, block = found_blocks[j]) {
if (!drv_data->can)
drv_data->can = scconf_get_str(block, "can", NULL);
if (!drv_data->st_dv_certificate
|| !drv_data->st_dv_certificate_len) {
file = scconf_get_str(block, "st_dv_certificate", NULL);
if (!fread_to_eof(file,
(unsigned char **) &drv_data->st_dv_certificate,
&drv_data->st_dv_certificate_len))
sc_log(ctx, "Waring: Could not read %s.\n", file);
}
if (!drv_data->st_certificate
|| !drv_data->st_certificate_len) {
file = scconf_get_str(block, "st_certificate", NULL);
if (!fread_to_eof(file,
(unsigned char **) &drv_data->st_certificate,
&drv_data->st_certificate_len))
sc_log(ctx, "Waring: Could not read %s.\n", file);
}
if (!drv_data->st_key
|| !drv_data->st_key_len) {
file = scconf_get_str(block, "st_key", NULL);
if (!fread_to_eof(file,
(unsigned char **) &drv_data->st_key,
&drv_data->st_key_len))
sc_log(ctx, "Waring: Could not read %s.\n", file);
}
}
free(found_blocks);
}
r = SC_SUCCESS;
err:
return r;
}
static int npa_match_card(sc_card_t * card)
{
if (_sc_match_atr(card, npa_atrs, &card->type) < 0)
return 0;
return 1;
}
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)
{
struct npa_drv_data *drv_data;
if (card->drv_data) {
drv_data = card->drv_data;
if (pace_output) {
pace_output->ef_cardaccess = drv_data->ef_cardaccess;
pace_output->ef_cardaccess_length = drv_data->ef_cardaccess_length;
}
if (pace_input && pace_input->pin_id == PACE_PIN_ID_CAN) {
pace_input->pin = (const unsigned char *) drv_data->can;
pace_input->pin_length = drv_data->can ? strlen(drv_data->can) : 0;
}
}
}
static void npa_get_cached_ta_params(sc_card_t *card,
const unsigned char *certs[2], size_t certs_lens[2],
const unsigned char **st_key, size_t *st_key_len)
{
struct npa_drv_data *drv_data;
size_t i;
if (card->drv_data) {
drv_data = card->drv_data;
if (certs && certs_lens) {
i = 0;
if (drv_data->st_dv_certificate) {
certs[i] = drv_data->st_dv_certificate;
certs_lens[i] = drv_data->st_dv_certificate_len;
i++;
}
if (drv_data->st_certificate) {
certs[i] = drv_data->st_certificate;
certs_lens[i] = drv_data->st_certificate_len;
}
}
if (st_key && st_key_len) {
*st_key = drv_data->st_key;
*st_key_len = drv_data->st_key_len;
}
}
}
static void npa_get_cached_ca_params(sc_card_t *card,
unsigned char **ef_cardsecurity, size_t *ef_cardsecurity_length)
{
struct npa_drv_data *drv_data;
if (card->drv_data) {
drv_data = card->drv_data;
if (ef_cardsecurity && ef_cardsecurity_length) {
*ef_cardsecurity = drv_data->ef_cardsecurity;
*ef_cardsecurity_length = drv_data->ef_cardsecurity_length;
}
}
}
static void npa_cache_or_free(sc_card_t *card,
unsigned char **ef_cardaccess, size_t *ef_cardaccess_length,
unsigned char **ef_cardsecurity, size_t *ef_cardsecurity_length)
{
struct npa_drv_data *drv_data;
if (card && 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;
}
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;
goto err;
}
/* 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, NULL);
if (r != SC_SUCCESS)
goto err;
r = _sc_card_add_ec_alg(card, 224, flags, ext_flags, NULL);
if (r != SC_SUCCESS)
goto err;
r = _sc_card_add_ec_alg(card, 256, flags, ext_flags, NULL);
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, NULL);
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)
{
sc_sm_stop(card);
npa_drv_data_free(card->drv_data);
card->drv_data = NULL;
EAC_cleanup();
return SC_SUCCESS;
}
static int npa_set_security_env(struct sc_card *card,
const struct sc_security_env *env, int se_num)
{
int r;
struct sc_card_driver *iso_drv;
struct sc_security_env fixed_env;
iso_drv = sc_get_iso7816_driver();
if (!env || !iso_drv || !iso_drv->ops || !iso_drv->ops->set_security_env) {
r = SC_ERROR_INTERNAL;
} else {
memcpy(&fixed_env, env, sizeof fixed_env);
if (env->operation == SC_SEC_OPERATION_SIGN) {
/* The pkcs#15 layer assumes that the field_size of the private key
* object is correctly initialized and wants to include it as
* algorithm reference. We disable it here */
fixed_env.flags &= ~SC_SEC_ENV_ALG_REF_PRESENT;
}
r = iso_drv->ops->set_security_env(card, &fixed_env, se_num);
}
return r;
}
static int npa_pin_cmd_get_info(struct sc_card *card,
struct sc_pin_cmd_data *data, int *tries_left)
{
int r;
u8 pin_reference;
if (!data || data->pin_type != SC_AC_CHV || !tries_left) {
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
pin_reference = data->pin_reference;
switch (data->pin_reference) {
case PACE_PIN_ID_CAN:
case PACE_PIN_ID_MRZ:
case PACE_PIN_ID_PUK:
/* usually unlimited number of retries */
*tries_left = -1;
data->pin1.max_tries = -1;
data->pin1.tries_left = -1;
r = SC_SUCCESS;
break;
case PACE_PIN_ID_PIN:
/* usually 3 tries */
*tries_left = 3;
data->pin1.max_tries = 3;
r = npa_pace_get_tries_left(card,
pin_reference, tries_left);
data->pin1.tries_left = *tries_left;
break;
default:
r = SC_ERROR_OBJECT_NOT_FOUND;
goto err;
}
err:
return r;
}
static int npa_pace_verify(struct sc_card *card,
unsigned char pin_reference, struct sc_pin_cmd_pin *pin,
const unsigned char *chat, size_t chat_length, int *tries_left)
{
int r;
struct establish_pace_channel_input pace_input;
struct establish_pace_channel_output pace_output;
memset(&pace_input, 0, sizeof pace_input);
memset(&pace_output, 0, sizeof pace_output);
if (chat) {
pace_input.chat = chat;
pace_input.chat_length = chat_length;
}
pace_input.pin_id = pin_reference;
if (pin) {
pace_input.pin = pin->data;
pace_input.pin_length = pin->len;
}
npa_get_cached_pace_params(card, &pace_input, &pace_output);
r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02);
if (tries_left) {
if (pace_output.mse_set_at_sw1 == 0x63
&& (pace_output.mse_set_at_sw2 & 0xc0) == 0xc0) {
*tries_left = pace_output.mse_set_at_sw2 & 0x0f;
} else {
*tries_left = -1;
}
}
/* resume the PIN if needed */
if (pin_reference == PACE_PIN_ID_PIN
&& r != SC_SUCCESS
&& pace_output.mse_set_at_sw1 == 0x63
&& (pace_output.mse_set_at_sw2 & 0xc0) == 0xc0
&& (pace_output.mse_set_at_sw2 & 0x0f) <= UC_PIN_SUSPENDED) {
/* TODO ask for user consent when automatically resuming the PIN */
sc_log(card->ctx, "%s is suspended. Will try to resume it with %s.\n",
npa_secret_name(pin_reference), npa_secret_name(PACE_PIN_ID_CAN));
pace_input.pin_id = PACE_PIN_ID_CAN;
pace_input.pin = NULL;
pace_input.pin_length = 0;
r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02);
if (r == SC_SUCCESS) {
pace_input.pin_id = pin_reference;
if (pin) {
pace_input.pin = pin->data;
pace_input.pin_length = pin->len;
}
r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02);
if (r == SC_SUCCESS) {
sc_log(card->ctx, "%s resumed.\n");
if (tries_left) {
*tries_left = MAX_PIN_TRIES;
}
} else {
if (tries_left) {
if (pace_output.mse_set_at_sw1 == 0x63
&& (pace_output.mse_set_at_sw2 & 0xc0) == 0xc0) {
*tries_left = pace_output.mse_set_at_sw2 & 0x0f;
} else {
*tries_left = -1;
}
}
}
}
}
if (pin_reference == PACE_PIN_ID_PIN && tries_left) {
if (*tries_left == 0) {
sc_log(card->ctx, "%s is suspended and must be resumed.\n",
npa_secret_name(pin_reference));
} else if (*tries_left == 1) {
sc_log(card->ctx, "%s is blocked and must be unblocked.\n",
npa_secret_name(pin_reference));
}
}
npa_cache_or_free(card, &pace_output.ef_cardaccess,
&pace_output.ef_cardaccess_length, NULL, NULL);
free(pace_output.recent_car);
free(pace_output.previous_car);
free(pace_output.id_icc);
free(pace_output.id_pcd);
return r;
}
static int npa_standard_pin_cmd(struct sc_card *card,
struct sc_pin_cmd_data *data, int *tries_left)
{
int r;
struct sc_card_driver *iso_drv;
iso_drv = sc_get_iso7816_driver();
if (!iso_drv || !iso_drv->ops || !iso_drv->ops->pin_cmd) {
r = SC_ERROR_INTERNAL;
} else {
r = iso_drv->ops->pin_cmd(card, data, tries_left);
}
return r;
}
static int npa_pin_cmd(struct sc_card *card,
struct sc_pin_cmd_data *data, int *tries_left)
{
int r;
if (!data) {
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
if (data->pin_type != SC_AC_CHV) {
r = SC_ERROR_NOT_SUPPORTED;
goto err;
}
switch (data->cmd) {
case SC_PIN_CMD_GET_INFO:
r = npa_pin_cmd_get_info(card, data, tries_left);
if (r != SC_SUCCESS)
goto err;
break;
case SC_PIN_CMD_UNBLOCK:
/* opensc-explorer unblocks the PIN by only sending
* SC_PIN_CMD_UNBLOCK whereas the PKCS#15 framework first verifies
* the PUK with SC_PIN_CMD_VERIFY and then calls with
* SC_PIN_CMD_UNBLOCK.
*
* Here we determine whether the PUK has been verified or not by
* checking if an SM channel has been established. */
if (card->sm_ctx.sm_mode != SM_MODE_TRANSMIT) {
/* PUK has not yet been verified */
r = npa_pace_verify(card, PACE_PIN_ID_PUK, &(data->pin1), NULL,
0, NULL);
if (r != SC_SUCCESS)
goto err;
}
r = npa_reset_retry_counter(card, data->pin_reference, 0,
NULL, 0);
if (r != SC_SUCCESS)
goto err;
break;
case SC_PIN_CMD_CHANGE:
case SC_PIN_CMD_VERIFY:
switch (data->pin_reference) {
case PACE_PIN_ID_CAN:
case PACE_PIN_ID_PUK:
case PACE_PIN_ID_MRZ:
case PACE_PIN_ID_PIN:
r = npa_pace_verify(card, data->pin_reference,
&(data->pin1), NULL, 0, tries_left);
if (r != SC_SUCCESS)
goto err;
break;
default:
/* assuming QES PIN */
/* We assume that the eSign application has already been
* unlocked, see npa_init().
*
* Now, verify the QES PIN. */
r = npa_standard_pin_cmd(card, data, tries_left);
if (r != SC_SUCCESS)
goto err;
break;
}
if (data->cmd == SC_PIN_CMD_CHANGE) {
r = npa_reset_retry_counter(card, data->pin_reference, 1,
(const char *) data->pin2.data, data->pin2.len);
if (r != SC_SUCCESS)
goto err;
}
break;
default:
r = SC_ERROR_INTERNAL;
goto err;
break;
}
err:
SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
}
static int npa_logout(sc_card_t *card)
{
struct sc_apdu apdu;
sc_sm_stop(card);
if (card->reader->capabilities & SC_READER_CAP_PACE_GENERIC) {
/* If PACE is done between reader and card, SM is transparent to us as
* it ends at the reader. With CLA=0x0C we provoque a SM error to
* disable SM on the reader. */
sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xA4, 0x00, 0x00);
apdu.cla = 0x0C;
sc_transmit_apdu(card, &apdu);
/* ignore result */
}
return sc_select_file(card, sc_get_mf_path(), NULL);
}
struct sc_card_driver *npa_get_driver(void)
{
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
npa_ops = *iso_drv->ops;
npa_ops.match_card = npa_match_card;
npa_ops.init = npa_init;
npa_ops.finish = npa_finish;
npa_ops.set_security_env = npa_set_security_env;
npa_ops.pin_cmd = npa_pin_cmd;
npa_ops.logout = npa_logout;
return &npa_drv;
}
void *sc_module_init(const char *name)
{
const char npa_name[] = "npa";
if (name) {
if (strcmp(npa_name, name) == 0)
return npa_get_driver;
}
return NULL;
}
const char *sc_driver_version(void)
{
/* Tested with OpenSC 0.12 and 0.13.0, which can't be captured by checking
* our version info against OpenSC's PACKAGE_VERSION. For this reason we
* tell OpenSC that everything is fine, here. */
return sc_get_version();
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright (C) 2014 Frank Morgner
*
* This file is part of npa.
*
* npa is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* npa 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* npa. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CARD_NPA_H
#define _CARD_NPA_H
#ifdef __cplusplus
extern "C" {
#endif
#include "libopensc/opensc.h"
enum {
SC_CARD_TYPE_NPA = 42000,
SC_CARD_TYPE_NPA_TEST,
SC_CARD_TYPE_NPA_ONLINE,
};
const unsigned char esign_chat[] = {
0x7F, 0x4C, 0x0E,
0x06, 0x09, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x03, 0x01, 0x02, 0x03,
0x53, 0x01, 0x03,
};
static const unsigned char df_esign_aid[] = { 0xa0, 0x00, 0x00, 0x01, 0x67, 0x45, 0x53, 0x49, 0x47, 0x4e};
static const unsigned char df_esign_path[] = { 0x3f, 0x00, 0x50, 0x15, 0x1f, 0xff};
static const unsigned char ef_cardaccess_path[] = { 0x3f, 0x00, 0x01, 0x1c};
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1 +0,0 @@
../../ccid/src/ccid-types.h

View File

@@ -1,138 +0,0 @@
/*
* Copyright (C) 2011 Frank Morgner
*
* This file is part of libnpa.
*
* libnpa is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libnpa 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* libnpa. If not, see <http://www.gnu.org/licenses/>.
*/
/* This example shows how to use the library functions perform_pace to
* get a secure channel to the nPA. We use the builtin function npa_change_pin
* to modify the PIN using the secure channel. Then we transmit an arbitrary
* APDU encrypted and authenticated to the card using sm_transmit_apdu. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <libopensc/sm.h>
#include <npa/iso-sm.h>
#include <npa/npa.h>
#include <npa/scutil.h>
#include <string.h>
static int reader_num = -1;
static const char *newpin = NULL;
static const char *pin = NULL;
/* SELECT the Master File (MF) */
const unsigned char apdubuf[] = {0x00, 0xA4, 0x00, 0x0C, 0x02, 0x3F, 0x00};
int
main (int argc, char **argv)
{
/* Set up the environment */
int r;
sc_context_t *ctx = NULL;
sc_card_t *card = NULL;
sc_reader_t *reader = NULL;
sc_apdu_t apdu;
u8 buf[0xffff];
struct establish_pace_channel_input pace_input;
struct establish_pace_channel_output pace_output;
memset(&pace_input, 0, sizeof pace_input);
memset(&pace_output, 0, sizeof pace_output);
/* Connect to a reader and the nPA */
r = initialize(reader_num, 0, &ctx, &reader);
if (r < 0) {
fprintf(stderr, "Can't initialize reader\n");
exit(1);
}
if (sc_connect_card(reader, &card) < 0) {
fprintf(stderr, "Could not connect to card\n");
sc_release_context(ctx);
exit(1);
}
/* initialize OpenPACE */
EAC_init();
/* Now we try to change the PIN. Therefor we need to establish a SM channel
* with PACE.
*
* You could set your PIN with pin=“123456”; or just leave it at NULL to be
* asked for it. The same applies to the new PIN newpin. */
pace_input.pin_id = PACE_PIN;
pace_input.pin = (unsigned char *) pin;
pace_input.pin_length = pin ? strlen(pin) : 0;
r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02);
if (r < 0)
goto err;
printf("Established PACE channel with PIN.\n");
r = npa_change_pin(card, newpin, newpin ? strlen(newpin) : 0);
if (r < 0)
goto err;
printf("Changed PIN.\n");
/* Now we want to transmit additional APDUs in the established SM channel.
*
* Here we are parsing the raw apdu buffer apdubuf to be transformed into
* an sc_apdu_t. Alternatively you could also set CLA, INS, P1, P2, ... by
* hand in the sc_apdu_t object. */
r = sc_bytes2apdu(ctx, apdubuf, sizeof apdubuf, &apdu);
if (r < 0)
goto err;
/* write the response data to buf */
apdu.resp = buf;
apdu.resplen = sizeof buf;
/* Transmit the APDU with SM */
r = sc_transmit_apdu(card, &apdu);
err:
fprintf(r < 0 ? stderr : stdout, "%s\n", sc_strerror(r));
/* Free up memory and wipe it if necessary (e.g. for keys stored in sm_ctx) */
if (pace_output.ef_cardaccess)
free(pace_output.ef_cardaccess);
if (pace_output.recent_car)
free(pace_output.recent_car);
if (pace_output.previous_car)
free(pace_output.previous_car);
if (pace_output.id_icc)
free(pace_output.id_icc);
if (pace_output.id_pcd)
free(pace_output.id_pcd);
sc_sm_stop(card);
sc_reset(card, 1);
sc_disconnect_card(card);
sc_release_context(ctx);
EAC_cleanup();
return -r;
}

View File

@@ -1,90 +0,0 @@
/*
* Copyright (C) 2014 Frank Morgner
*
* This file is part of npa.
*
* npa is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* npa 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* npa. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @defgroup sm Interface to Secure Messaging (SM) defined in ISO 7816
* @{
*/
#ifndef _CCID_SM_INTERNAL_H
#define _CCID_SM_INTERNAL_H
#include <libopensc/opensc.h>
#ifdef __cplusplus
extern "C" {
#endif
/* @brief Protect an APDU with Secure Messaging
*
* If secure messaging (SM) is activated in \a sctx and \a apdu is not already
* SM protected, \a apdu is processed with the following steps:
* \li call to \a sctx->pre_transmit
* \li encrypt \a apdu calling \a sctx->encrypt
* \li authenticate \a apdu calling \a sctx->authenticate
* \li copy the SM protected data to \a sm_apdu
*
* Data for authentication or encryption is always padded before the callback
* functions are called
*
* @param[in] card
* @param[in] apdu
* @param[in,out] sm_apdu
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int iso_get_sm_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu);
/* @brief Remove Secure Messaging from an APDU
*
* If secure messaging (SM) is activated in \a sctx and \a apdu is not already
* SM protected, \a apdu is processed with the following steps:
* \li verify SM protected \a apdu calling \a sctx->verify_authentication
* \li decrypt SM protected \a apdu calling \a sctx->decrypt
* \li copy decrypted/authenticated data and status bytes to \a apdu
*
* Callback functions must not remove padding.
*
* @param[in] card
* @param[in,out] apdu
* @param[in,out] sm_apdu will be freed when done.
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int iso_free_sm_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu);
/**
* @brief Cleans up allocated ressources of the ISO SM driver
*
* \c iso_sm_close() is designed as SM card operation. However, have in mind
* that this card operation is not called automatically for \c
* sc_disconnect_card() .
*
* @param[in] card
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int iso_sm_close(struct sc_card *card);
#ifdef __cplusplus
}
#endif
#endif
/* @} */

View File

@@ -1,749 +0,0 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of libnpa.
*
* libnpa is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libnpa 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* libnpa. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef ENABLE_SM
#include "iso-sm-internal.h"
#include <libopensc/asn1.h>
#include <libopensc/log.h>
#include <npa/iso-sm.h>
#include <npa/scutil.h>
#include <stdlib.h>
#include <string.h>
static const struct sc_asn1_entry c_sm_capdu[] = {
{ "Padding-content indicator followed by cryptogram",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x07, SC_ASN1_OPTIONAL, NULL, NULL },
{ "Protected Le",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x17, SC_ASN1_OPTIONAL, NULL, NULL },
{ "Cryptographic Checksum",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x0E, SC_ASN1_OPTIONAL, NULL, NULL },
{ NULL , 0 , 0 , 0 , NULL , NULL }
};
static const struct sc_asn1_entry c_sm_rapdu[] = {
{ "Padding-content indicator followed by cryptogram" ,
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x07, SC_ASN1_OPTIONAL, NULL, NULL },
{ "Processing Status",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x19, 0 , NULL, NULL },
{ "Cryptographic Checksum",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x0E, SC_ASN1_OPTIONAL, NULL, NULL },
{ NULL, 0, 0, 0, NULL, NULL }
};
static int
add_iso_pad(const u8 *data, size_t datalen, int block_size, u8 **padded)
{
u8 *p;
size_t p_len;
if (!padded)
return SC_ERROR_INVALID_ARGUMENTS;
/* calculate length of padded message */
p_len = (datalen / block_size) * block_size + block_size;
p = realloc(*padded, p_len);
if (!p)
return SC_ERROR_OUT_OF_MEMORY;
if (*padded != data)
/* Flawfinder: ignore */
memcpy(p, data, datalen);
*padded = p;
/* now add iso padding */
memset(p + datalen, 0x80, 1);
memset(p + datalen + 1, 0, p_len - datalen - 1);
return p_len;
}
static int
add_padding(const struct iso_sm_ctx *ctx, const u8 *data, size_t datalen,
u8 **padded)
{
u8 *p;
switch (ctx->padding_indicator) {
case SM_NO_PADDING:
if (*padded != data) {
p = realloc(*padded, datalen);
if (!p)
return SC_ERROR_OUT_OF_MEMORY;
*padded = p;
/* Flawfinder: ignore */
memcpy(*padded, data, datalen);
}
return datalen;
case SM_ISO_PADDING:
return add_iso_pad(data, datalen, ctx->block_length, padded);
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
}
static int
rm_padding(u8 padding_indicator, const u8 *data, size_t datalen)
{
if (!datalen || !data)
return SC_ERROR_INVALID_ARGUMENTS;
size_t len;
switch (padding_indicator) {
case SM_NO_PADDING:
len = datalen;
break;
case SM_ISO_PADDING:
len = datalen;
while (len) {
len--;
if (data[len])
break;
}
if (data[len] != 0x80)
return SC_ERROR_INVALID_DATA;
break;
default:
return SC_ERROR_NOT_SUPPORTED;
}
return len;
}
static int format_le(size_t le, struct sc_asn1_entry *le_entry,
u8 **lebuf, size_t *le_len)
{
u8 *p;
if (!lebuf || !le_len)
return SC_ERROR_INVALID_ARGUMENTS;
p = realloc(*lebuf, *le_len);
if (!p)
return SC_ERROR_OUT_OF_MEMORY;
*lebuf = p;
switch (*le_len) {
case 1:
p[0] = le;
break;
case 2:
p[0] = le >> 8;
p[1] = le & 0xff;
break;
case 3:
p[0] = 0x00;
p[1] = le >> 8;
p[2] = le & 0xff;
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
sc_format_asn1_entry(le_entry, *lebuf, le_len, SC_ASN1_PRESENT);
return SC_SUCCESS;
}
static int prefix_buf(u8 prefix, u8 *buf, size_t buflen, u8 **cat)
{
u8 *p;
p = realloc(*cat, buflen + 1);
if (!p)
return SC_ERROR_OUT_OF_MEMORY;
if (*cat == buf) {
memmove(p + 1, p, buflen);
} else {
/* Flawfinder: ignore */
memcpy(p + 1, buf, buflen);
}
p[0] = prefix;
*cat = p;
return buflen + 1;
}
static int format_data(sc_card_t *card, const struct iso_sm_ctx *ctx,
const u8 *data, size_t datalen,
struct sc_asn1_entry *formatted_encrypted_data_entry,
u8 **formatted_data, size_t *formatted_data_len)
{
int r;
u8 *pad_data = NULL;
size_t pad_data_len = 0;
if (!ctx || !formatted_data || !formatted_data_len) {
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
r = add_padding(ctx, data, datalen, &pad_data);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not add padding to data: %s",
sc_strerror(r));
goto err;
}
pad_data_len = r;
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Data to encrypt", pad_data, pad_data_len);
r = ctx->encrypt(card, ctx, pad_data, pad_data_len, formatted_data);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not encrypt the data");
goto err;
}
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Cryptogram", *formatted_data, r);
r = prefix_buf(ctx->padding_indicator, *formatted_data, r, formatted_data);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not prepend padding indicator to formatted "
"data: %s", sc_strerror(r));
goto err;
}
*formatted_data_len = r;
sc_format_asn1_entry(formatted_encrypted_data_entry,
*formatted_data, formatted_data_len, SC_ASN1_PRESENT);
r = SC_SUCCESS;
err:
if (pad_data) {
sc_mem_clear(pad_data, pad_data_len);
free(pad_data);
}
return r;
}
static int format_head(const struct iso_sm_ctx *ctx, const sc_apdu_t *apdu,
u8 **formatted_head)
{
if (!apdu || !formatted_head)
return SC_ERROR_INVALID_ARGUMENTS;
u8 *p = realloc(*formatted_head, 4);
if (!p)
return SC_ERROR_OUT_OF_MEMORY;
p[0] = apdu->cla;
p[1] = apdu->ins;
p[2] = apdu->p1;
p[3] = apdu->p2;
*formatted_head = p;
return add_padding(ctx, *formatted_head, 4, formatted_head);
}
static int sm_encrypt(const struct iso_sm_ctx *ctx, sc_card_t *card,
const sc_apdu_t *apdu, sc_apdu_t **psm_apdu)
{
struct sc_asn1_entry sm_capdu[4];
u8 *p, *le = NULL, *sm_data = NULL, *fdata = NULL, *mac_data = NULL,
*asn1 = NULL, *mac = NULL, *resp_data = NULL;
size_t sm_data_len, fdata_len, mac_data_len, asn1_len, mac_len, le_len;
int r, cse;
sc_apdu_t *sm_apdu = NULL;
if (!apdu || !ctx || !card || !card->reader || !psm_apdu) {
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
if ((apdu->cla & 0x0C) == 0x0C) {
r = SC_ERROR_INVALID_ARGUMENTS;
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Given APDU is already protected with some secure messaging");
goto err;
}
sc_copy_asn1_entry(c_sm_capdu, sm_capdu);
sm_apdu = malloc(sizeof(sc_apdu_t));
if (!sm_apdu) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
sm_apdu->control = apdu->control;
sm_apdu->flags = apdu->flags;
sm_apdu->cla = apdu->cla|0x0C;
sm_apdu->ins = apdu->ins;
sm_apdu->p1 = apdu->p1;
sm_apdu->p2 = apdu->p2;
r = format_head(ctx, sm_apdu, &mac_data);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format header of SM apdu");
goto err;
}
mac_data_len = r;
/* get le and data depending on the case of the insecure command */
cse = apdu->cse;
if ((apdu->le/ctx->block_length + 1)*ctx->block_length + 18 > 0xff+1)
/* for encrypted APDUs we usually get authenticated status bytes (4B),
* a MAC (11B) and a cryptogram with padding indicator (3B without
* data). The cryptogram is always padded to the block size. */
cse |= SC_APDU_EXT;
switch (cse) {
case SC_APDU_CASE_1:
break;
case SC_APDU_CASE_2_SHORT:
le_len = 1;
r = format_le(apdu->le, sm_capdu + 1, &le, &le_len);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format Le of SM apdu");
goto err;
}
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Protected Le (plain)", le, le_len);
break;
case SC_APDU_CASE_2_EXT:
if (card->reader->active_protocol == SC_PROTO_T0) {
/* T0 extended APDUs look just like short APDUs */
le_len = 1;
r = format_le(apdu->le, sm_capdu + 1, &le, &le_len);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format Le of SM apdu");
goto err;
}
} else {
/* in case of T1 always use 2 bytes for length */
le_len = 2;
r = format_le(apdu->le, sm_capdu + 1, &le, &le_len);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format Le of SM apdu");
goto err;
}
}
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Protected Le (plain)", le, le_len);
break;
case SC_APDU_CASE_3_SHORT:
case SC_APDU_CASE_3_EXT:
r = format_data(card, ctx, apdu->data, apdu->datalen,
sm_capdu + 0, &fdata, &fdata_len);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format data of SM apdu");
goto err;
}
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Padding-content indicator followed by cryptogram (plain)",
fdata, fdata_len);
break;
case SC_APDU_CASE_4_SHORT:
/* in case of T0 no Le byte is added */
if (card->reader->active_protocol != SC_PROTO_T0) {
le_len = 1;
r = format_le(apdu->le, sm_capdu + 1, &le, &le_len);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format Le of SM apdu");
goto err;
}
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Protected Le (plain)", le, le_len);
}
r = format_data(card, ctx, apdu->data, apdu->datalen,
sm_capdu + 0, &fdata, &fdata_len);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format data of SM apdu");
goto err;
}
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Padding-content indicator followed by cryptogram (plain)",
fdata, fdata_len);
break;
case SC_APDU_CASE_4_EXT:
if (card->reader->active_protocol == SC_PROTO_T0) {
/* again a T0 extended case 4 APDU looks just
* like a short APDU, the additional data is
* transferred using ENVELOPE and GET RESPONSE */
} else {
/* only 2 bytes are use to specify the length of the
* expected data */
le_len = 2;
r = format_le(apdu->le, sm_capdu + 1, &le, &le_len);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format Le of SM apdu");
goto err;
}
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Protected Le (plain)", le, le_len);
}
r = format_data(card, ctx, apdu->data, apdu->datalen,
sm_capdu + 0, &fdata, &fdata_len);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not format data of SM apdu");
goto err;
}
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Padding-content indicator followed by cryptogram (plain)",
fdata, fdata_len);
break;
default:
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Unhandled apdu case");
r = SC_ERROR_INVALID_DATA;
goto err;
}
r = sc_asn1_encode(card->ctx, sm_capdu, (u8 **) &asn1, &asn1_len);
if (r < 0) {
goto err;
}
if (asn1_len) {
p = realloc(mac_data, mac_data_len + asn1_len);
if (!p) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
mac_data = p;
/* Flawfinder: ignore */
memcpy(mac_data + mac_data_len, asn1, asn1_len);
mac_data_len += asn1_len;
r = add_padding(ctx, mac_data, mac_data_len, &mac_data);
if (r < 0) {
goto err;
}
mac_data_len = r;
}
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Data to authenticate", mac_data, mac_data_len);
r = ctx->authenticate(card, ctx, mac_data, mac_data_len,
&mac);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not get authentication code");
goto err;
}
mac_len = r;
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Cryptographic Checksum (plain)", mac, mac_len);
/* format SM apdu */
sc_format_asn1_entry(sm_capdu + 2, mac, &mac_len, SC_ASN1_PRESENT);
r = sc_asn1_encode(card->ctx, sm_capdu, (u8 **) &sm_data, &sm_data_len);
if (r < 0)
goto err;
sm_apdu->data = sm_data;
sm_apdu->datalen = sm_data_len;
sm_apdu->lc = sm_data_len;
sm_apdu->le = 0;
if (cse & SC_APDU_EXT) {
sm_apdu->cse = SC_APDU_CASE_4_EXT;
#if OPENSC_NOT_BOGUS_ANYMORE
sm_apdu->resplen = 0xffff+1;
#else
sm_apdu->resplen = SC_MAX_EXT_APDU_BUFFER_SIZE;
#endif
} else {
sm_apdu->cse = SC_APDU_CASE_4_SHORT;
#if OPENSC_NOT_BOGUS_ANYMORE
sm_apdu->resplen = 0xff+1;
#else
sm_apdu->resplen = SC_MAX_APDU_BUFFER_SIZE;
#endif
}
resp_data = malloc(sm_apdu->resplen);
if (!resp_data) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
sm_apdu->resp = resp_data;
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "ASN.1 encoded encrypted APDU data", sm_apdu->data, sm_apdu->datalen);
*psm_apdu = sm_apdu;
err:
free(fdata);
free(asn1);
free(mac_data);
free(mac);
free(le);
if (r < 0) {
free(resp_data);
free(sm_apdu);
free(sm_data);
}
return r;
}
static int sm_decrypt(const struct iso_sm_ctx *ctx, sc_card_t *card,
const sc_apdu_t *sm_apdu, sc_apdu_t *apdu)
{
int r;
struct sc_asn1_entry sm_rapdu[4];
struct sc_asn1_entry my_sm_rapdu[4];
u8 sw[2], mac[8], fdata[SC_MAX_EXT_APDU_BUFFER_SIZE];
size_t sw_len = sizeof sw, mac_len = sizeof mac, fdata_len = sizeof fdata,
buf_len, asn1_len;
const u8 *buf;
u8 *data = NULL, *mac_data = NULL, *asn1 = NULL;
sc_copy_asn1_entry(c_sm_rapdu, sm_rapdu);
sc_format_asn1_entry(sm_rapdu + 0, fdata, &fdata_len, 0);
sc_format_asn1_entry(sm_rapdu + 1, sw, &sw_len, 0);
sc_format_asn1_entry(sm_rapdu + 2, mac, &mac_len, 0);
r = sc_asn1_decode(card->ctx, sm_rapdu, sm_apdu->resp, sm_apdu->resplen,
&buf, &buf_len);
if (r < 0)
goto err;
if (buf_len > 0) {
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
goto err;
}
if (sm_rapdu[2].flags & SC_ASN1_PRESENT) {
/* copy from sm_apdu to my_sm_apdu, but leave mac at default */
sc_copy_asn1_entry(sm_rapdu, my_sm_rapdu);
sc_copy_asn1_entry(&c_sm_rapdu[2], &my_sm_rapdu[2]);
r = sc_asn1_encode(card->ctx, my_sm_rapdu, &asn1, &asn1_len);
if (r < 0)
goto err;
r = add_padding(ctx, asn1, asn1_len, &mac_data);
if (r < 0) {
goto err;
}
r = ctx->verify_authentication(card, ctx, mac, mac_len,
mac_data, r);
if (r < 0)
goto err;
} else {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Cryptographic Checksum missing");
r = SC_ERROR_ASN1_OBJECT_NOT_FOUND;
goto err;
}
if (sm_rapdu[0].flags & SC_ASN1_PRESENT) {
if (ctx->padding_indicator != fdata[0]) {
r = SC_ERROR_UNKNOWN_DATA_RECEIVED;
goto err;
}
r = ctx->decrypt(card, ctx, fdata + 1, fdata_len - 1, &data);
if (r < 0)
goto err;
buf_len = r;
r = rm_padding(ctx->padding_indicator, data, buf_len);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not remove padding");
goto err;
}
if (apdu->resplen < r || (r && !apdu->resp)) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"Response of SM APDU %u byte%s too long", r-apdu->resplen,
r-apdu->resplen < 2 ? "" : "s");
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
/* Flawfinder: ignore */
memcpy(apdu->resp, data, r);
apdu->resplen = r;
} else {
apdu->resplen = 0;
}
if (sm_rapdu[1].flags & SC_ASN1_PRESENT) {
if (sw_len != 2) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Length of processing status bytes must be 2");
r = SC_ERROR_ASN1_END_OF_CONTENTS;
goto err;
}
apdu->sw1 = sw[0];
apdu->sw2 = sw[1];
} else {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Authenticated status bytes are missing");
r = SC_ERROR_ASN1_OBJECT_NOT_FOUND;
goto err;
}
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Decrypted APDU sw1=%02x sw2=%02x",
apdu->sw1, apdu->sw2);
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Decrypted APDU response data",
apdu->resp, apdu->resplen);
r = SC_SUCCESS;
err:
free(asn1);
free(mac_data);
if (data) {
sc_mem_clear(data, buf_len);
free(data);
}
return r;
}
static int iso_add_sm(struct iso_sm_ctx *sctx, sc_card_t *card,
sc_apdu_t *apdu, sc_apdu_t **sm_apdu)
{
if (!card || !sctx)
return SC_ERROR_INVALID_ARGUMENTS;
if ((apdu->cla & 0x0C) == 0x0C) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Given APDU is already protected with some secure messaging. Closing own SM context.");
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sc_sm_stop(card),
"Could not close ISO SM session");
return SC_ERROR_SM_NOT_APPLIED;
}
if (sctx->pre_transmit)
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sctx->pre_transmit(card, sctx, apdu),
"Could not complete SM specific pre transmit routine");
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sm_encrypt(sctx, card, apdu, sm_apdu),
"Could not encrypt APDU");
return SC_SUCCESS;
}
static int iso_rm_sm(struct iso_sm_ctx *sctx, sc_card_t *card,
sc_apdu_t *sm_apdu, sc_apdu_t *apdu)
{
if (sctx->post_transmit)
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sctx->post_transmit(card, sctx, sm_apdu),
"Could not complete SM specific post transmit routine");
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sm_decrypt(sctx, card, sm_apdu, apdu),
"Could not decrypt APDU");
if (sctx->finish)
SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, sctx->finish(card, sctx, apdu),
"Could not complete SM specific post transmit routine");
return SC_SUCCESS;
}
int iso_sm_close(struct sc_card *card)
{
if (card) {
iso_sm_ctx_clear_free(card->sm_ctx.info.cmd_data);
card->sm_ctx.info.cmd_data = NULL;
}
return SC_SUCCESS;
}
int iso_get_sm_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu)
{
return iso_add_sm(card->sm_ctx.info.cmd_data, card, apdu, sm_apdu);
}
int iso_free_sm_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu)
{
if (!sm_apdu)
return SC_ERROR_INVALID_ARGUMENTS;
struct sc_apdu *p = *sm_apdu;
int r = iso_rm_sm(card->sm_ctx.info.cmd_data, card, p, apdu);
if (p) {
free((unsigned char *) p->data);
free((unsigned char *) p->resp);
}
free(*sm_apdu);
*sm_apdu = NULL;
return r;
}
struct iso_sm_ctx *iso_sm_ctx_create(void)
{
struct iso_sm_ctx *sctx = malloc(sizeof *sctx);
if (!sctx)
return NULL;
sctx->priv_data = NULL;
sctx->padding_indicator = SM_ISO_PADDING;
sctx->block_length = 0;
sctx->authenticate = NULL;
sctx->verify_authentication = NULL;
sctx->encrypt = NULL;
sctx->decrypt = NULL;
sctx->pre_transmit = NULL;
sctx->post_transmit = NULL;
sctx->finish = NULL;
sctx->clear_free = NULL;
return sctx;
}
void iso_sm_ctx_clear_free(struct iso_sm_ctx *sctx)
{
if (sctx && sctx->clear_free)
sctx->clear_free(sctx);
free(sctx);
}
int iso_sm_start(struct sc_card *card, struct iso_sm_ctx *sctx)
{
if (!card)
return SC_ERROR_INVALID_ARGUMENTS;
if (card->sm_ctx.ops.close)
card->sm_ctx.ops.close(card);
card->sm_ctx.info.cmd_data = sctx;
card->sm_ctx.ops.close = iso_sm_close;
card->sm_ctx.ops.free_sm_apdu = iso_free_sm_apdu;
card->sm_ctx.ops.get_sm_apdu = iso_get_sm_apdu;
card->sm_ctx.sm_mode = SM_MODE_TRANSMIT;
return SC_SUCCESS;
}
#else
int iso_sm_close(struct sc_card *card)
{
return SC_ERROR_NOT_SUPPORTED;
}
int iso_get_sm_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu)
{
return SC_ERROR_NOT_SUPPORTED;
}
struct iso_sm_ctx *iso_sm_ctx_create(void)
{
return NULL;
}
void iso_sm_ctx_clear_free(struct iso_sm_ctx *sctx)
{
}
int iso_sm_start(struct sc_card *card, struct iso_sm_ctx *sctx)
{
return SC_ERROR_NOT_SUPPORTED;
}
#endif

View File

@@ -1,804 +0,0 @@
/*
* Copyright (C) 2010-2012 Frank Morgner <frankmorgner@gmail.com>
*
* This file is part of libnpa.
*
* libnpa is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libnpa 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* libnpa. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cmdline.h"
#include "sslutil.h"
#include <eac/pace.h>
#include <libopensc/log.h>
#include <libopensc/opensc.h>
#include <libopensc/sm.h>
#include <npa/boxing.h>
#include <npa/iso-sm.h>
#include <npa/npa.h>
#include <npa/scutil.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#ifndef HAVE_GETLINE
static ssize_t getline(char **lineptr, size_t *n, FILE *stream)
{
if (!lineptr)
return -1;
char *p = realloc(*lineptr, SC_MAX_EXT_APDU_BUFFER_SIZE*3);
if (!p)
return -1;
*lineptr = p;
if (fgets(p, SC_MAX_EXT_APDU_BUFFER_SIZE*3, stream) == NULL)
return -1;
return strlen(p);
}
#endif
static void read_dg(sc_card_t *card, unsigned char sfid, const char *dg_str,
unsigned char **dg, size_t *dg_len)
{
int r = read_binary_rec(card, sfid, dg, dg_len);
if (r < 0)
fprintf(stderr, "Coult not read DG %02u %s (%s)\n",
sfid, dg_str, sc_strerror(r));
else {
fprintf(stdout, "Read ");
bin_print(stdout, dg_str, *dg, *dg_len);
}
}
static void write_dg(sc_card_t *card, unsigned char sfid, const char *dg_str,
const char *dg_hex)
{
unsigned char dg[0xff];
size_t dg_len = sizeof dg;
int r;
r = sc_hex_to_bin(dg_hex, dg, &dg_len);
if (r < 0) {
fprintf(stderr, "Could not parse DG %02u %s (%s)\n",
sfid, dg_str, sc_strerror(r));
} else {
r = write_binary_rec(card, sfid, dg, dg_len);
if (r < 0)
fprintf(stderr, "Could not write DG %02u %s (%s)\n",
sfid, dg_str, sc_strerror(r));
else
printf("Wrote DG %02u %s\n", sfid, dg_str);
}
}
#define ISO_VERIFY 0x20
static void verify(sc_card_t *card, const char *verify_str,
unsigned char *data, size_t data_len)
{
sc_apdu_t apdu;
int r;
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, ISO_VERIFY, 0x80, 0);
apdu.cla = 0x80;
apdu.data = data;
apdu.datalen = data_len;
apdu.lc = data_len;
r = sc_transmit_apdu(card, &apdu);
if (r < 0)
fprintf(stderr, "Coult not verify %s (%s)\n",
verify_str, sc_strerror(r));
else
printf("Verified %s\n", verify_str);
}
int npa_translate_apdus(sc_card_t *card, FILE *input)
{
u8 buf[4 + 3 + 0xffff + 3];
char *read = NULL;
size_t readlen = 0, apdulen;
sc_apdu_t apdu;
ssize_t linelen;
int r;
memset(&apdu, 0, sizeof apdu);
while (1) {
if (input == stdin)
printf("Enter unencrypted C-APDU (empty line to exit)\n");
linelen = getline(&read, &readlen, input);
if (linelen <= 1) {
if (linelen < 0) {
r = SC_ERROR_INTERNAL;
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
"Could not read line");
} else {
r = SC_SUCCESS;
printf("Thanks for flying with ccid\n");
}
break;
}
read[linelen - 1] = 0;
apdulen = sizeof buf;
if (sc_hex_to_bin(read, buf, &apdulen) < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
"Could not format binary string");
continue;
}
if (input != stdin)
bin_print(stdout, "Unencrypted C-APDU", buf, apdulen);
r = sc_bytes2apdu(card->ctx, buf, apdulen, &apdu);
if (r < 0) {
bin_log(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid C-APDU", buf, apdulen);
continue;
}
apdu.resp = buf;
apdu.resplen = sizeof buf;
r = sc_transmit_apdu(card, &apdu);
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE_TOOL,
"Could not send C-APDU: %s", sc_strerror(r));
continue;
}
printf("Decrypted R-APDU sw1=%02x sw2=%02x\n", apdu.sw1, apdu.sw2);
bin_print(stdout, "Decrypted R-APDU response data", apdu.resp, apdu.resplen);
printf("======================================================================\n");
}
if (read)
free(read);
return r;
}
static int add_to_ASN1_AUXILIARY_DATA(
ASN1_AUXILIARY_DATA **auxiliary_data,
int nid, const unsigned char *data, size_t data_len)
{
int r;
CVC_DISCRETIONARY_DATA_TEMPLATE *template = NULL;
if (!auxiliary_data) {
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
if (!*auxiliary_data) {
*auxiliary_data = ASN1_AUXILIARY_DATA_new();
if (!*auxiliary_data) {
r = SC_ERROR_INTERNAL;
goto err;
}
}
template = CVC_DISCRETIONARY_DATA_TEMPLATE_new();
if (!template) {
r = SC_ERROR_INTERNAL;
goto err;
}
template->type = OBJ_nid2obj(nid);
if (!template->type) {
r = SC_ERROR_INTERNAL;
goto err;
}
if (data && data_len) {
template->discretionary_data3 = ASN1_OCTET_STRING_new();
if (!template->discretionary_data3
|| !M_ASN1_OCTET_STRING_set(
template->discretionary_data3, data, data_len)) {
r = SC_ERROR_INTERNAL;
goto err;
}
}
if (!sk_push((_STACK*) (*auxiliary_data), template)) {
r = SC_ERROR_INTERNAL;
goto err;
}
r = SC_SUCCESS;
err:
return r;
}
int
main (int argc, char **argv)
{
const char *newpin = NULL;
const char *pin = NULL;
const char *puk = NULL;
const char *can = NULL;
const char *mrz = NULL;
unsigned char chat[0xff];
unsigned char desc[0xffff];
unsigned char **certs = NULL;
size_t *certs_lens = NULL;
unsigned char *privkey = NULL;
size_t privkey_len = 0;
unsigned char auxiliary_data[0xff];
size_t auxiliary_data_len = 0;
unsigned char community_id[0xf];
size_t community_id_len = 0;
sc_context_t *ctx = NULL;
sc_card_t *card = NULL;
sc_reader_t *reader;
int r, tr_version = EAC_TR_VERSION_2_02;
struct establish_pace_channel_input pace_input;
struct establish_pace_channel_output pace_output;
struct timeval tv;
size_t i;
FILE *input = NULL;
CVC_CERT *cvc_cert = NULL;
unsigned char *certs_chat = NULL;
unsigned char *dg = NULL;
size_t dg_len = 0;
ASN1_AUXILIARY_DATA *templates = NULL;
unsigned char *ef_cardsecurity = NULL;
size_t ef_cardsecurity_len = 0;
struct gengetopt_args_info cmdline;
memset(&pace_input, 0, sizeof pace_input);
memset(&pace_output, 0, sizeof pace_output);
/* Parse command line */
if (cmdline_parser (argc, argv, &cmdline) != 0)
exit(1);
if (cmdline.env_flag) {
can = getenv("CAN");
mrz = getenv("MRZ");
pin = getenv("PIN");
puk = getenv("PUK");
newpin = getenv("NEWPIN");
}
can = cmdline.can_arg;
mrz = cmdline.mrz_arg;
pin = cmdline.pin_arg;
puk = cmdline.puk_arg;
newpin = cmdline.new_pin_arg;
if (cmdline.chat_given) {
pace_input.chat = chat;
pace_input.chat_length = sizeof chat;
if (sc_hex_to_bin(cmdline.chat_arg, (u8 *) pace_input.chat,
&pace_input.chat_length) < 0) {
fprintf(stderr, "Could not parse CHAT.\n");
exit(2);
}
}
if (cmdline.cert_desc_given) {
pace_input.certificate_description = desc;
pace_input.certificate_description_length = sizeof desc;
if (sc_hex_to_bin(cmdline.cert_desc_arg,
(u8 *) pace_input.certificate_description,
&pace_input.certificate_description_length) < 0) {
fprintf(stderr, "Could not parse certificate description.\n");
exit(2);
}
}
if (cmdline.tr_03110v201_flag)
tr_version = EAC_TR_VERSION_2_01;
if (cmdline.disable_all_checks_flag)
npa_default_flags |= NPA_FLAG_DISABLE_CHECK_ALL;
if (cmdline.disable_ta_checks_flag)
npa_default_flags |= NPA_FLAG_DISABLE_CHECK_TA;
if (cmdline.disable_ca_checks_flag)
npa_default_flags |= NPA_FLAG_DISABLE_CHECK_CA;
if (cmdline.info_flag)
return print_avail(cmdline.verbose_given);
r = initialize(cmdline.reader_arg, cmdline.verbose_given, &ctx, &reader);
if (r < 0) {
fprintf(stderr, "Can't initialize reader\n");
exit(1);
}
if (sc_connect_card(reader, &card) < 0) {
fprintf(stderr, "Could not connect to card\n");
sc_release_context(ctx);
exit(1);
}
#ifdef DISABLE_GLOBAL_BOXING_INITIALIZATION
sc_detect_boxing_cmds(card->reader);
#else
sc_initialize_boxing_cmds(ctx);
#endif
EAC_init();
if (cmdline.cvc_dir_given)
EAC_set_cvc_default_dir(cmdline.cvc_dir_arg);
if (cmdline.x509_dir_given)
EAC_set_x509_default_dir(cmdline.cvc_dir_arg);
if (cmdline.break_flag) {
/* The biggest buffer sprintf could write with "%llu" */
char secretbuf[strlen("18446744073709551615")+1];
unsigned long long secret = 0;
unsigned long long maxsecret = 0;
if (cmdline.pin_given) {
pace_input.pin_id = PACE_PIN;
pace_input.pin_length = 6;
maxsecret = 999999;
if (pin) {
if (sscanf(pin, "%llu", &secret) != 1) {
fprintf(stderr, "%s is not an unsigned long long.\n",
npa_secret_name(pace_input.pin_id));
exit(2);
}
if (strlen(pin) > pace_input.pin_length) {
fprintf(stderr, "%s too big, only %u digits allowed.\n",
npa_secret_name(pace_input.pin_id),
(unsigned int) pace_input.pin_length);
exit(2);
}
}
} else if (cmdline.can_given) {
pace_input.pin_id = PACE_CAN;
pace_input.pin_length = 6;
maxsecret = 999999;
if (can) {
if (sscanf(can, "%llu", &secret) != 1) {
fprintf(stderr, "%s is not an unsigned long long.\n",
npa_secret_name(pace_input.pin_id));
exit(2);
}
if (strlen(can) > pace_input.pin_length) {
fprintf(stderr, "%s too big, only %u digits allowed.\n",
npa_secret_name(pace_input.pin_id),
(unsigned int) pace_input.pin_length);
exit(2);
}
}
} else if (cmdline.puk_given) {
pace_input.pin_id = PACE_PUK;
pace_input.pin_length = 10;
maxsecret = 9999999999LLU;
if (puk) {
if (sscanf(puk, "%llu", &secret) != 1) {
fprintf(stderr, "%s is not an unsigned long long.\n",
npa_secret_name(pace_input.pin_id));
exit(2);
}
if (strlen(puk) > pace_input.pin_length) {
fprintf(stderr, "%s too big, only %u digits allowed.\n",
npa_secret_name(pace_input.pin_id),
(unsigned int) pace_input.pin_length);
exit(2);
}
}
} else {
fprintf(stderr, "Please specify whether to do PACE with "
"PIN, CAN or PUK.\n");
exit(1);
}
pace_input.pin = (unsigned char *) secretbuf;
do {
sprintf(secretbuf, "%0*llu", (unsigned int) pace_input.pin_length, secret);
gettimeofday(&tv, NULL);
printf("%u,%06u: Trying %s=%s\n",
(unsigned int) tv.tv_sec, (unsigned int) tv.tv_usec,
npa_secret_name(pace_input.pin_id), pace_input.pin);
r = perform_pace(card, pace_input, &pace_output, tr_version);
secret++;
} while (0 > r && secret <= maxsecret);
gettimeofday(&tv, NULL);
if (0 > r) {
printf("%u,%06u: Tried breaking %s without success.\n",
(unsigned int) tv.tv_sec, (unsigned int) tv.tv_usec,
npa_secret_name(pace_input.pin_id));
goto err;
} else {
printf("%u,%06u: Tried breaking %s with success (=%s).\n",
(unsigned int) tv.tv_sec, (unsigned int) tv.tv_usec,
npa_secret_name(pace_input.pin_id),
pace_input.pin);
}
}
if (cmdline.resume_flag) {
pace_input.pin_id = PACE_CAN;
if (can) {
pace_input.pin = (unsigned char *) can;
pace_input.pin_length = strlen(can);
} else {
pace_input.pin = NULL;
pace_input.pin_length = 0;
}
r = perform_pace(card, pace_input, &pace_output, tr_version);
if (r < 0)
goto err;
printf("Established PACE channel with CAN.\n");
pace_input.pin_id = PACE_PIN;
if (pin) {
pace_input.pin = (unsigned char *) pin;
pace_input.pin_length = strlen(pin);
} else {
pace_input.pin = NULL;
pace_input.pin_length = 0;
}
r = perform_pace(card, pace_input, &pace_output, tr_version);
if (r < 0)
goto err;
printf("Established PACE channel with PIN. PIN resumed.\n");
}
if (cmdline.unblock_flag) {
pace_input.pin_id = PACE_PUK;
if (puk) {
pace_input.pin = (unsigned char *) puk;
pace_input.pin_length = strlen(puk);
} else {
pace_input.pin = NULL;
pace_input.pin_length = 0;
}
r = perform_pace(card, pace_input, &pace_output, tr_version);
if (r < 0)
goto err;
printf("Established PACE channel with PUK.\n");
r = npa_unblock_pin(card);
if (r < 0)
goto err;
printf("Unblocked PIN.\n");
}
if (cmdline.new_pin_given) {
pace_input.pin_id = PACE_PIN;
if (pin) {
pace_input.pin = (unsigned char *) pin;
pace_input.pin_length = strlen(pin);
} else {
pace_input.pin = NULL;
pace_input.pin_length = 0;
}
r = perform_pace(card, pace_input, &pace_output, tr_version);
if (r < 0)
goto err;
printf("Established PACE channel with PIN.\n");
r = npa_change_pin(card, newpin, newpin ? strlen(newpin) : 0);
if (r < 0)
goto err;
printf("Changed PIN.\n");
}
if (cmdline.translate_given
|| (!cmdline.resume_flag && !cmdline.new_pin_given
&& !cmdline.unblock_flag && !cmdline.break_given)) {
if (cmdline.cv_certificate_given || cmdline.private_key_given
|| cmdline.auxiliary_data_given) {
if (!cmdline.cv_certificate_given || !cmdline.private_key_given) {
fprintf(stderr, "Need at least the terminal's certificate "
"and its private key to perform terminal authentication.\n");
exit(1);
}
certs = calloc(sizeof *certs, cmdline.cv_certificate_given + 1);
certs_lens = calloc(sizeof *certs_lens,
cmdline.cv_certificate_given + 1);
if (!certs || !certs_lens) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
for (i = 0; i < cmdline.cv_certificate_given; i++) {
if (!fread_to_eof(cmdline.cv_certificate_arg[i],
(unsigned char **) &certs[i], &certs_lens[i])) {
fprintf(stderr, "Could not read certificate.\n");
r = SC_ERROR_INVALID_DATA;
goto err;
}
}
if (!pace_input.chat_length) {
const unsigned char *p = certs[cmdline.cv_certificate_given-1];
if (!CVC_d2i_CVC_CERT(&cvc_cert, &p, certs_lens[cmdline.cv_certificate_given-1])
|| !cvc_cert || !cvc_cert->body
|| !cvc_cert->body->certificate_authority_reference
|| !cvc_cert->body->chat) {
fprintf(stderr, "Could not parse certificate.\n");
ssl_error(ctx);
r = SC_ERROR_INVALID_DATA;
goto err;
}
pace_input.chat_length = i2d_CVC_CHAT(cvc_cert->body->chat, &certs_chat);
if (0 >= (int) pace_input.chat_length) {
fprintf(stderr, "Could not parse CHAT.\n");
r = SC_ERROR_INVALID_DATA;
ssl_error(ctx);
goto err;
}
pace_input.chat = certs_chat;
}
if (!fread_to_eof(cmdline.private_key_arg,
&privkey, &privkey_len)) {
fprintf(stderr, "Could not parse private key.\n");
r = SC_ERROR_INVALID_DATA;
goto err;
}
if (cmdline.auxiliary_data_given) {
auxiliary_data_len = sizeof auxiliary_data;
if (sc_hex_to_bin(cmdline.auxiliary_data_arg, auxiliary_data,
&auxiliary_data_len) < 0) {
fprintf(stderr, "Could not parse auxiliary data.\n");
r = SC_ERROR_INVALID_DATA;
goto err;
}
} else {
if (cmdline.older_than_given) {
r = add_to_ASN1_AUXILIARY_DATA(&templates,
NID_id_DateOfBirth,
(unsigned char *) cmdline.older_than_arg,
strlen(cmdline.older_than_arg));
if (r < 0)
goto err;
}
if (cmdline.verify_validity_given) {
r = add_to_ASN1_AUXILIARY_DATA(&templates,
NID_id_DateOfExpiry,
(unsigned char *) cmdline.verify_validity_arg,
strlen(cmdline.verify_validity_arg));
if (r < 0)
goto err;
}
if (cmdline.verify_community_given) {
community_id_len = sizeof community_id;
if (sc_hex_to_bin(cmdline.verify_community_arg, community_id,
&community_id_len) < 0) {
fprintf(stderr, "Could not parse community ID.\n");
exit(2);
}
r = add_to_ASN1_AUXILIARY_DATA(&templates,
NID_id_CommunityID,
community_id, community_id_len);
if (r < 0)
goto err;
}
if (templates) {
unsigned char *p = NULL;
auxiliary_data_len = i2d_ASN1_AUXILIARY_DATA(
templates, &p);
if (0 > (int) auxiliary_data_len
|| auxiliary_data_len > sizeof auxiliary_data) {
free(p);
fprintf(stderr, "Auxiliary data too big.\n");
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
memcpy(auxiliary_data, p, auxiliary_data_len);
free(p);
}
}
}
pace_input.pin = NULL;
pace_input.pin_length = 0;
if (cmdline.pin_given) {
pace_input.pin_id = PACE_PIN;
if (pin) {
pace_input.pin = (unsigned char *) pin;
pace_input.pin_length = strlen(pin);
}
} else if (cmdline.can_given) {
pace_input.pin_id = PACE_CAN;
if (can) {
pace_input.pin = (unsigned char *) can;
pace_input.pin_length = strlen(can);
}
} else if (cmdline.mrz_given) {
pace_input.pin_id = PACE_MRZ;
if (mrz) {
pace_input.pin = (unsigned char *) mrz;
pace_input.pin_length = strlen(mrz);
}
} else if (cmdline.puk_given) {
pace_input.pin_id = PACE_PUK;
if (puk) {
pace_input.pin = (unsigned char *) puk;
pace_input.pin_length = strlen(puk);
}
} else {
fprintf(stderr, "Skipping PIN verification\n");
goto nopace;
}
r = perform_pace(card, pace_input, &pace_output, tr_version);
if (r < 0)
goto err;
printf("Established PACE channel with %s.\n",
npa_secret_name(pace_input.pin_id));
nopace:
if (cmdline.cv_certificate_given || cmdline.private_key_given) {
r = perform_terminal_authentication(card,
(const unsigned char **) certs, certs_lens,
privkey, privkey_len, auxiliary_data, auxiliary_data_len);
if (r < 0)
goto err;
printf("Performed Terminal Authentication.\n");
r = perform_chip_authentication(card, &ef_cardsecurity, &ef_cardsecurity_len);
if (r < 0)
goto err;
printf("Performed Chip Authentication.\n");
unsigned char eid_aid[] = { 0xE8, 0x07, 0x04, 0x00, 0x7f, 0x00, 0x07, 0x03, 0x02};
sc_path_t path;
sc_path_set(&path, SC_PATH_TYPE_DF_NAME, eid_aid, sizeof eid_aid, 0, 0);
r = sc_select_file(card, &path, NULL);
if (r < 0)
goto err;
printf("Selected eID application.\n");
}
if (cmdline.read_dg1_flag)
read_dg(card, 1, "Document Type", &dg, &dg_len);
if (cmdline.read_dg2_flag)
read_dg(card, 2, "Issuing State", &dg, &dg_len);
if (cmdline.read_dg3_flag)
read_dg(card, 3, "Date of Expiry", &dg, &dg_len);
if (cmdline.read_dg4_flag)
read_dg(card, 4, "Given Names", &dg, &dg_len);
if (cmdline.read_dg5_flag)
read_dg(card, 5, "Family Names", &dg, &dg_len);
if (cmdline.read_dg6_flag)
read_dg(card, 6, "Religious/Artistic Name", &dg, &dg_len);
if (cmdline.read_dg7_flag)
read_dg(card, 7, "Academic Title", &dg, &dg_len);
if (cmdline.read_dg8_flag)
read_dg(card, 8, "Date of Birth", &dg, &dg_len);
if (cmdline.read_dg9_flag)
read_dg(card, 9, "Place of Birth", &dg, &dg_len);
if (cmdline.read_dg10_flag)
read_dg(card, 10, "Nationality", &dg, &dg_len);
if (cmdline.read_dg11_flag)
read_dg(card, 11, "Sex", &dg, &dg_len);
if (cmdline.read_dg12_flag)
read_dg(card, 12, "Optional Data", &dg, &dg_len);
if (cmdline.read_dg13_flag)
read_dg(card, 13, "Birth Name", &dg, &dg_len);
if (cmdline.read_dg14_flag)
read_dg(card, 14, "DG 14", &dg, &dg_len);
if (cmdline.read_dg15_flag)
read_dg(card, 15, "DG 15", &dg, &dg_len);
if (cmdline.read_dg16_flag)
read_dg(card, 16, "DG 16", &dg, &dg_len);
if (cmdline.read_dg17_flag)
read_dg(card, 17, "Normal Place of Residence", &dg, &dg_len);
if (cmdline.read_dg18_flag)
read_dg(card, 18, "Community ID", &dg, &dg_len);
if (cmdline.read_dg19_flag)
read_dg(card, 19, "Residence Permit I", &dg, &dg_len);
if (cmdline.read_dg20_flag)
read_dg(card, 20, "Residence Permit II", &dg, &dg_len);
if (cmdline.read_dg21_flag)
read_dg(card, 21, "Optional Data", &dg, &dg_len);
if (cmdline.write_dg17_given)
write_dg(card, 17, "Normal Place of Residence", cmdline.write_dg17_arg);
if (cmdline.write_dg18_given)
write_dg(card, 18, "Community ID", cmdline.write_dg18_arg);
if (cmdline.write_dg19_given)
write_dg(card, 19, "Residence Permit I", cmdline.write_dg19_arg);
if (cmdline.write_dg20_given)
write_dg(card, 20, "Residence Permit II", cmdline.write_dg20_arg);
if (cmdline.write_dg21_given)
write_dg(card, 21, "Optional Data", cmdline.write_dg21_arg);
if (cmdline.older_than_given) {
unsigned char id_DateOfBirth[] = {6, 9, 4, 0, 127, 0, 7, 3, 1, 4, 1};
verify(card, "age", id_DateOfBirth, sizeof id_DateOfBirth);
}
if (cmdline.verify_validity_given) {
unsigned char id_DateOfExpiry[] = {6, 9, 4, 0, 127, 0, 7, 3, 1, 4, 2};
verify(card, "validity", id_DateOfExpiry, sizeof id_DateOfExpiry);
}
if (cmdline.verify_community_given) {
unsigned char id_CommunityID[] = {6, 9, 4, 0, 127, 0, 7, 3, 1, 4, 3};
verify(card, "community ID", id_CommunityID, sizeof id_CommunityID);
}
if (cmdline.translate_given) {
if (strncmp(cmdline.translate_arg, "stdin", strlen("stdin")) == 0)
input = stdin;
else {
input = fopen(cmdline.translate_arg, "r");
if (!input) {
perror("Opening file with APDUs");
r = SC_ERROR_INVALID_DATA;
goto err;
}
}
r = npa_translate_apdus(card, input);
if (r < 0)
goto err;
fclose(input);
input = NULL;
}
}
err:
cmdline_parser_free(&cmdline);
free(pace_output.ef_cardaccess);
free(pace_output.recent_car);
free(pace_output.previous_car);
free(pace_output.id_icc);
free(pace_output.id_pcd);
if (ef_cardsecurity) {
OPENSSL_cleanse(ef_cardsecurity, ef_cardsecurity_len);
free(ef_cardsecurity);
}
if (input)
fclose(input);
if (certs) {
i = 0;
while (certs[i]) {
free((unsigned char *) certs[i]);
i++;
}
free(certs);
}
free(certs_lens);
free(certs_chat);
if (cvc_cert)
CVC_CERT_free(cvc_cert);
free(privkey);
free(dg);
if (templates)
ASN1_AUXILIARY_DATA_free(templates);
sc_sm_stop(card);
sc_reset(card, 1);
sc_disconnect_card(card);
sc_release_context(ctx);
EAC_cleanup();
if (r < 0)
fprintf(stderr, "Error: %s\n", sc_strerror(r));
return -r;
}

View File

@@ -1,228 +0,0 @@
package "npa-tool"
purpose "@PACKAGE_SUMMARY@"
option "info" i
"Print available readers and drivers."
flag off
option "reader" r
"Number of the PC/SC reader to use (-1 for autodetect)"
int
default="-1"
optional
option "verbose" v
"Use (several times) to be more verbose"
multiple
optional
section "Password Authenticated Connection Establishment (PACE)"
option "pin" p
"Run PACE with (transport) eID-PIN"
string
argoptional
optional
option "puk" u
"Run PACE with PUK"
string
argoptional
optional
option "can" c
"Run PACE with CAN"
string
argoptional
optional
option "mrz" m
"Run PACE with MRZ (insert MRZ without newlines)"
string
argoptional
optional
option "env" -
"Whether to use environment variables PIN, PUK, CAN, MRZ and NEWPIN. You may want to clean your environment before enabling this."
flag off
section "PIN management"
option "new-pin" N
"Install a new PIN"
string
argoptional
optional
option "resume" R
"Resume eID-PIN (uses CAN to activate last retry)"
flag off
option "unblock" U
"Unblock PIN (uses PUK to activate three more retries)"
flag off
section "Terminal Authentication (TA) and Chip Authentication (CA)"
option "cv-certificate" C
"Card Verifiable Certificate to create a certificate chain. Can be used multiple times (order is important)."
string
typestr="FILENAME"
optional
multiple
option "cert-desc" -
"Certificate description to show for Terminal Authentication"
string
typestr="HEX_STRING"
optional
option "chat" -
"Card holder authorization template to use (default is terminal's CHAT). Use 7F4C0E060904007F000703010203530103 to trigger EAC on the CAT-C (Komfortleser)."
string
typestr="HEX_STRING"
optional
option "auxiliary-data" A
"Terminal's auxiliary data (default is determined by verification of validity, age and community ID)."
string
typestr="HEX_STRING"
optional
option "private-key" P
"Terminal's private key"
string
typestr="FILENAME"
optional
option "cvc-dir" -
"Where to look for the CVCA's certificate"
string
typestr="DIRECTORY"
default="@CVCDIR@"
optional
option "x509-dir" -
"Where to look for the CSCA's certificate"
string
typestr="DIRECTORY"
default="@X509DIR@"
optional
option "disable-ta-checks" -
"Disable checking the validity period of CV certifcates"
flag off
option "disable-ca-checks" -
"Disable passive authentication"
flag off
section "Read and write data groups"
option "read-dg1" -
"Read DG 1 (Document Type)"
flag off
option "read-dg2" -
"Read DG 2 (Issuing State)"
flag off
option "read-dg3" -
"Read DG 3 (Date of Expiry)"
flag off
option "read-dg4" -
"Read DG 4 (Given Names)"
flag off
option "read-dg5" -
"Read DG 5 (Family Names)"
flag off
option "read-dg6" -
"Read DG 6 (Religious/Artistic Name)"
flag off
option "read-dg7" -
"Read DG 7 (Academic Title)"
flag off
option "read-dg8" -
"Read DG 8 (Date of Birth)"
flag off
option "read-dg9" -
"Read DG 9 (Place of Birth)"
flag off
option "read-dg10" -
"Read DG 10 (Nationality)"
flag off
option "read-dg11" -
"Read DG 11 (Sex)"
flag off
option "read-dg12" -
"Read DG 12 (Optional Data)"
flag off
option "read-dg13" -
"Read DG 13 (Birth Name)"
flag off
option "read-dg14" -
"Read DG 14"
flag off
option "read-dg15" -
"Read DG 15"
flag off
option "read-dg16" -
"Read DG 16"
flag off
option "read-dg17" -
"Read DG 17 (Normal Place of Residence)"
flag off
option "read-dg18" -
"Read DG 18 (Community ID)"
flag off
option "read-dg19" -
"Read DG 19 (Residence Permit I)"
flag off
option "read-dg20" -
"Read DG 20 (Residence Permit II)"
flag off
option "read-dg21" -
"Read DG 21 (Optional Data)"
flag off
option "write-dg17" -
"Write DG 17 (Normal Place of Residence)"
string
typestr="HEX_STRING"
optional
option "write-dg18" -
"Write DG 18 (Community ID)"
string
typestr="HEX_STRING"
optional
option "write-dg19" -
"Write DG 19 (Residence Permit I)"
string
typestr="HEX_STRING"
optional
option "write-dg20" -
"Write DG 20 (Residence Permit II)"
string
typestr="HEX_STRING"
optional
option "write-dg21" -
"Write DG 21 (Optional Data)"
string
typestr="HEX_STRING"
optional
section "Verification of validity, age and community ID"
option "verify-validity" -
"Verify chip's validity with a reference date"
string
typestr="YYYYMMDD"
optional
option "older-than" -
"Verify age with a reference date"
string
typestr="YYYYMMDD"
optional
option "verify-community" -
"Verify community ID with a reference ID"
string
typestr="HEX_STRING"
optional
section "Special options, not always useful"
option "break" b
"Brute force PIN, CAN or PUK. Use together with -p, -a or -u"
flag off
option "translate" t
"File with APDUs of HEX_STRINGs to send through the secure channel"
string
typestr="FILENAME"
default="stdin"
optional
option "tr-03110v201" -
"Force compliance to BSI TR-03110 version 2.01"
flag off
option "disable-all-checks" -
"Disable all checking of fly-by-data"
flag off
text "
Report bugs to @PACKAGE_BUGREPORT@
Written by Frank Morgner <frankmorgner@gmail.com>"

File diff suppressed because it is too large Load Diff

View File

@@ -1,67 +0,0 @@
/*
* boxing.c: interface related to boxing commands with pseudo APDUs
*
* Copyright (C) 2013 Frank Morgner <frankmorgner@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _BOXING_CMDS_H
#define _BOXING_CMDS_H
#include "libopensc/opensc.h"
#include "libopensc/pace.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DISABLE_GLOBAL_BOXING_INITIALIZATION
#ifndef DISABLE_GLOBAL_BOXING_INITIALIZATION
/** initialize boxing for all available readers once on the application
* layer */
/* The OpenSC PC/SC reader implementation allows a transmit only when a
* card is connected. So testing for boxing commands will yield an error
* then. */
void sc_initialize_boxing_cmds(sc_context_t *ctx);
#else
void sc_detect_boxing_cmds(sc_reader_t *reader);
#endif
int boxing_pace_input_to_buf(sc_context_t *ctx,
const struct establish_pace_channel_input *input,
unsigned char **asn1, size_t *asn1_len);
int boxing_buf_to_pace_input(sc_context_t *ctx,
const unsigned char *asn1, size_t asn1_len,
struct establish_pace_channel_input *input);
int boxing_pace_output_to_buf(sc_context_t *ctx,
const struct establish_pace_channel_output *output,
unsigned char **asn1, size_t *asn1_len);
int boxing_buf_to_pace_output(sc_context_t *ctx,
const unsigned char *asn1, size_t asn1_len,
struct establish_pace_channel_output *output);
int boxing_pace_capabilities_to_buf(sc_context_t *ctx,
const unsigned long sc_reader_t_capabilities,
unsigned char **asn1, size_t *asn1_len);
int boxing_buf_to_pace_capabilities(sc_context_t *ctx,
const unsigned char *asn1, size_t asn1_len,
unsigned long *sc_reader_t_capabilities);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,125 +0,0 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of npa.
*
* npa is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* npa 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* npa. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @defgroup sm Interface to Secure Messaging (SM) defined in ISO 7816
* @{
*/
#ifndef _CCID_SM_H
#define _CCID_SM_H
#include <libopensc/opensc.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @brief maximum length of response when targeting a SM RAPDU
*
* Using SM with authenticated data+le and encrypted data this is the biggest
* amount of the unencrypted response data we can receive. We assume AES block
* length for padding and MAC. */
#define MAX_SM_APDU_RESP_SIZE 223
/** @brief maximum length of data when targeting a SM APDU
*
* Using SM with authenticated data+header and encrypted data this is the
* biggest amount of the unencrypted data we can send. We assume AES block
* length for padding and MAC. */
#define MAX_SM_APDU_DATA_SIZE 239
/** @brief Padding indicator: use ISO/IEC 9797-1 padding method 2 */
#define SM_ISO_PADDING 0x01
/** @brief Padding indicator: use no padding */
#define SM_NO_PADDING 0x02
/** @brief Secure messaging context */
struct iso_sm_ctx {
/** @brief data of the specific crypto implementation */
void *priv_data;
/** @brief Padding-content indicator byte (ISO 7816-4 Table 30) */
u8 padding_indicator;
/** @brief Pad to this block length */
size_t block_length;
/** @brief Call back function for authentication of data */
int (*authenticate)(sc_card_t *card, const struct iso_sm_ctx *ctx,
const u8 *data, size_t datalen, u8 **outdata);
/** @brief Call back function for verifying authentication data */
int (*verify_authentication)(sc_card_t *card, const struct iso_sm_ctx *ctx,
const u8 *mac, size_t maclen,
const u8 *macdata, size_t macdatalen);
/** @brief Call back function for encryption of data */
int (*encrypt)(sc_card_t *card, const struct iso_sm_ctx *ctx,
const u8 *data, size_t datalen, u8 **enc);
/** @brief Call back function for decryption of data */
int (*decrypt)(sc_card_t *card, const struct iso_sm_ctx *ctx,
const u8 *enc, size_t enclen, u8 **data);
/** @brief Call back function for actions before encoding and encryption of \a apdu */
int (*pre_transmit)(sc_card_t *card, const struct iso_sm_ctx *ctx,
sc_apdu_t *apdu);
/** @brief Call back function for actions before decryption and decoding of \a sm_apdu */
int (*post_transmit)(sc_card_t *card, const struct iso_sm_ctx *ctx,
sc_apdu_t *sm_apdu);
/** @brief Call back function for actions after decrypting SM protected APDU */
int (*finish)(sc_card_t *card, const struct iso_sm_ctx *ctx,
sc_apdu_t *apdu);
/** @brief Clears and frees private data */
void (*clear_free)(const struct iso_sm_ctx *ctx);
};
/**
* @brief Clears and frees the SM context including private data
*
* Calls \a sctx->clear_free() if available
*
* @param[in] sctx (optional)
*/
void iso_sm_ctx_clear_free(struct iso_sm_ctx *sctx);
/**
* @brief Creates a SM context
*
* @return SM context or NULL if an error occurred
*/
struct iso_sm_ctx *iso_sm_ctx_create(void);
/**
* @brief Initializes a card for usage of the ISO SM driver
*
* If a SM module has been assigned previously to the card, it will be cleaned
* up.
*
* @param[in] card
* @param[in] sctx will NOT be freed automatically. \a sctx should be present
* for the time of the SM session.
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int iso_sm_start(struct sc_card *card, struct iso_sm_ctx *sctx);
#ifdef __cplusplus
}
#endif
#endif
/* @} */

View File

@@ -1,257 +0,0 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of npa.
*
* npa is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* npa 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* npa. If not, see <http://www.gnu.org/licenses/>.
*/
/*
*/
/**
* @file
* @defgroup npa Interface to German identity card (neuer Personalausweis, nPA)
* @{
*/
#ifndef _CCID_NPA_H
#define _CCID_NPA_H
#include <eac/cv_cert.h>
#include <eac/eac.h>
#include <eac/pace.h>
#include <libopensc/opensc.h>
#include <libopensc/pace.h>
#include <npa/iso-sm.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @brief NPA capabilities (TR-03119): PACE */
#define NPA_BITMAP_PACE 0x40
/** @brief NPA capabilities (TR-03119): EPA: eID */
#define NPA_BITMAP_EID 0x20
/** @brief NPA capabilities (TR-03119): EPA: eSign */
#define NPA_BITMAP_ESIGN 0x10
/** @brief NPA result (TR-03119): Kein Fehler */
#define NPA_SUCCESS 0x00000000
/** @brief NPA result (TR-03119): Längen im Input sind inkonsistent */
#define NPA_ERROR_LENGTH_INCONSISTENT 0xD0000001
/** @brief NPA result (TR-03119): Unerwartete Daten im Input */
#define NPA_ERROR_UNEXPECTED_DATA 0xD0000002
/** @brief NPA result (TR-03119): Unerwartete Kombination von Daten im Input */
#define NPA_ERROR_UNEXPECTED_DATA_COMBINATION 0xD0000003
/** @brief NPA result (TR-03119): Die Karte unterstützt das PACE Verfahren nicht. (Unerwartete Struktur in Antwortdaten der Karte) */
#define NPA_ERROR_CARD_NOT_SUPPORTED 0xE0000001
/** @brief NPA result (TR-03119): Der Kartenleser unterstützt den angeforderten bzw. den ermittelten Algorithmus nicht. */
#define NPA_ERROR_ALGORITH_NOT_SUPPORTED 0xE0000002
/** @brief NPA result (TR-03119): Der Kartenleser kennt die PIN ID nicht. */
#define NPA_ERROR_PINID_NOT_SUPPORTED 0xE0000003
/** @brief NPA result (TR-03119): Negative Antwort der Karte auf Select EF_CardAccess (needs to be OR-ed with SW1|SW2) */
#define NPA_ERROR_SELECT_EF_CARDACCESS 0xF0000000
/** @brief NPA result (TR-03119): Negative Antwort der Karte auf Read Binary (needs to be OR-ed with SW1|SW2) */
#define NPA_ERROR_READ_BINARY 0xF0010000
/** @brief NPA result (TR-03119): Negative Antwort der Karte auf MSE: Set AT (needs to be OR-ed with SW1|SW2) */
#define NPA_ERROR_MSE_SET_AT 0xF0020000
/** @brief NPA result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 1 (needs to be OR-ed with SW1|SW2) */
#define NPA_ERROR_GENERAL_AUTHENTICATE_1 0xF0030000
/** @brief NPA result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 2 (needs to be OR-ed with SW1|SW2) */
#define NPA_ERROR_GENERAL_AUTHENTICATE_2 0xF0040000
/** @brief NPA result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 3 (needs to be OR-ed with SW1|SW2) */
#define NPA_ERROR_GENERAL_AUTHENTICATE_3 0xF0050000
/** @brief NPA result (TR-03119): Negative Antwort der Karte auf General Authenticate Step 4 (needs to be OR-ed with SW1|SW2) */
#define NPA_ERROR_GENERAL_AUTHENTICATE_4 0xF0060000
/** @brief NPA result (TR-03119): Kommunikationsabbruch mit Karte. */
#define NPA_ERROR_COMMUNICATION 0xF0100001
/** @brief NPA result (TR-03119): Keine Karte im Feld. */
#define NPA_ERROR_NO_CARD 0xF0100002
/** @brief NPA result (TR-03119): Benutzerabbruch. */
#define NPA_ERROR_ABORTED 0xF0200001
/** @brief NPA result (TR-03119): Benutzer Timeout */
#define NPA_ERROR_TIMEOUT 0xF0200002
/** @brief File identifier of EF.CardAccess */
#define FID_EF_CARDACCESS 0x011C
/** @brief Short file identifier of EF.CardAccess */
#define SFID_EF_CARDACCESS 0x1C
/** @brief File identifier of EF.CardSecurity */
#define FID_EF_CARDSECURITY 0x011D
/** @brief Short file identifier of EF.CardAccess */
#define SFID_EF_CARDSECURITY 0x1D
/** @brief Maximum length of PIN */
#define MAX_PIN_LEN 6
/** @brief Minimum length of PIN */
#define MIN_PIN_LEN 6
/** @brief Length of CAN */
#define CAN_LEN 6
/** @brief Minimum length of MRZ */
#define MAX_MRZ_LEN 128
/** @brief Number of retries for PIN */
#define MAX_PIN_TRIES 3
/** @brief Usage counter of PIN in suspended state */
#define UC_PIN_SUSPENDED 1
/**
* @brief Names the type of the PACE secret
*
* @param pin_id type of the PACE secret
*
* @return Printable string containing the name
*/
const char *npa_secret_name(enum s_type pin_id);
/**
* @brief Get the PACE capabilities
*
* @param[in,out] bitmap where to store capabilities bitmap
* @note Since this code offers no support for terminal certificate, the bitmap is always \c PACE_BITMAP_PACE|PACE_BITMAP_EID
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int get_pace_capabilities(u8 *bitmap);
/**
* @brief Establish secure messaging using PACE
*
* Modifies \a card to use the ISO SM driver and initializes the data
* structures to use the established SM channel.
*
* Prints certificate description and card holder authorization template if
* given in a human readable form to stdout. If no secret is given, the user is
* asked for it. Only \a pace_input.pin_id is mandatory, the other members of
* \a pace_input can be set to \c 0 or \c NULL respectively.
*
* The buffers in \a pace_output are allocated using \c realloc() and should be
* set to NULL, if empty. If an EF.CardAccess is already present, this file is
* reused and not fetched from the card.
*
* @param[in,out] card
* @param[in] pace_input
* @param[in,out] pace_output
* @param[in] tr_version Version of TR-03110 to use with PACE
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int perform_pace(sc_card_t *card,
struct establish_pace_channel_input pace_input,
struct establish_pace_channel_output *pace_output,
enum eac_tr_version tr_version);
/**
* @brief Terminal Authentication version 2
*
* @param[in] card
* @param[in] certs chain of cv certificates, the last certificate
* is the terminal's certificate, array should be
* terminated with \c NULL
* @param[in] certs_lens length of each element in \c certs, should be
* terminated with \c 0
* @param[in] privkey The terminal's private key
* @param[in] privkey_len length of \a privkey
* @param[in] auxiliary_data auxiliary data for age/validity/community ID
* verification. Should be an ASN1 object tagged
* with \c 0x67
* @param[in] auxiliary_data_len length of \a auxiliary_data
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int perform_terminal_authentication(sc_card_t *card,
const unsigned char **certs, const size_t *certs_lens,
const unsigned char *privkey, size_t privkey_len,
const unsigned char *auxiliary_data, size_t auxiliary_data_len);
/**
* @brief Establish secure messaging using Chip Authentication version 2
*
* Switches the SM context of \c card to the new established keys.
*
* @param[in] card
* @param[in,out] ef_cardsecurity
* @param[in,out] ef_cardsecurity_len
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int perform_chip_authentication(sc_card_t *card,
unsigned char **ef_cardsecurity, size_t *ef_cardsecurity_len);
/**
* @brief Sends a reset retry counter APDU
*
* According to TR-03110 the reset retry counter APDU is used to set a new PIN
* or to reset the retry counter of the PIN. The standard requires this
* operation to be authorized either by an established PACE channel or by the
* effective authorization of the terminal's certificate.
*
* @param[in] card
* @param[in] pin_id Type of secret (usually PIN or CAN). You may use <tt>enum s_type</tt> from \c <openssl/pace.h>.
* @param[in] ask_for_secret whether to ask the user for the secret (\c 1) or not (\c 0)
* @param[in] new (optional) new secret
* @param[in] new_len (optional) length of \a new
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int npa_reset_retry_counter(sc_card_t *card,
enum s_type pin_id, int ask_for_secret,
const char *new, size_t new_len);
/**
* @brief Sends an MSE:Set AT to determine the number of remaining tries
*
* @param[in] card
* @param[in] pin_id Type of secret (usually PIN or CAN). You may use <tt>enum s_type</tt> from \c <openssl/pace.h>.
* @param[in,out] tries_left Tries left or -1 if no specific number has been returned by the card (e.g. when there is no limit in retries).
*
* @return \c SC_SUCCESS or error code if an error occurred
*/
int npa_pace_get_tries_left(sc_card_t *card,
enum s_type pin_id, int *tries_left);
/**
* @brief Send APDU to unblock the PIN
*
* @param[in] card
*/
#define npa_unblock_pin(card) \
npa_reset_retry_counter(card, PACE_PIN, 0, NULL, 0)
/**
* @brief Send APDU to set a new PIN
*
* @param[in] card
* @param[in] newp (optional) new PIN
* @param[in] newplen (optional) length of \a new
*/
#define npa_change_pin(card, newp, newplen) \
npa_reset_retry_counter(card, PACE_PIN, 1, newp, newplen)
/** @brief Disable all sanity checks done by libnpa */
#define NPA_FLAG_DISABLE_CHECK_ALL 1
/** @brief Disable checking validity period of CV certificates */
#define NPA_FLAG_DISABLE_CHECK_TA 2
/** @brief Disable checking passive authentication during CA */
#define NPA_FLAG_DISABLE_CHECK_CA 4
/** @brief Use \c npa_default_flags to disable checks for EAC/SM */
extern char npa_default_flags;
/** @brief ASN.1 type for authenticated auxiliary data for terminal authentication */
typedef STACK_OF(CVC_DISCRETIONARY_DATA_TEMPLATE) ASN1_AUXILIARY_DATA;
DECLARE_ASN1_FUNCTIONS(ASN1_AUXILIARY_DATA)
#ifdef __cplusplus
}
#endif
#endif
/* @} */

Submodule npa/src/opensc deleted from 6de3f93e9b

View File

@@ -1,345 +0,0 @@
/*
* Copyright (C) 2010 Frank Morgner
*
* This file is part of libnpa.
*
* libnpa is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libnpa 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* libnpa. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "libopensc/internal.h"
#ifndef HAVE__SC_MATCH_ATR
int sc_dlclose(void *handle) {}
const char *sc_dlerror() {}
void *sc_dlsym(void *handle, const char *symbol) {}
size_t strlcpy(char *dst, const char *src, size_t siz) {}
int sc_mutex_create(const sc_context_t *ctx, void **mutex) {}
int sc_mutex_destroy(const sc_context_t *ctx, void *mutex) {}
int _sc_parse_atr(sc_reader_t *reader) {}
void *sc_dlopen(const char *filename) {}
int sc_mutex_lock(const sc_context_t *ctx, void *mutex) {}
int sc_mutex_unlock(const sc_context_t *ctx, void *mutex) {}
#include "libopensc/card.c"
#endif
#if !defined(HAVE_SC_APDU_GET_OCTETS) || !defined(HAVE_SC_APDU_SET_RESP)
#ifdef HAVE__SC_MATCH_ATR
size_t sc_get_max_send_size(const sc_card_t *card) {}
#endif
#include "libopensc/apdu.c"
#endif
#include <libopensc/log.h>
#include <npa/iso-sm.h>
#include <npa/scutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int initialize(int reader_id, int verbose,
sc_context_t **ctx, sc_reader_t **reader)
{
unsigned int i, reader_count;
if (!ctx || !reader)
return SC_ERROR_INVALID_ARGUMENTS;
int r = sc_establish_context(ctx, "");
if (r < 0 || !*ctx) {
fprintf(stderr, "Failed to create initial context: %s", sc_strerror(r));
return r;
}
(*ctx)->debug = verbose;
(*ctx)->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER;
reader_count = sc_ctx_get_reader_count(*ctx);
if (reader_count == 0) {
sc_debug(*ctx, SC_LOG_DEBUG_NORMAL, "No reader not found.\n");
return SC_ERROR_NO_READERS_FOUND;
}
if (reader_id < 0) {
/* Automatically try to skip to a reader with a card if reader not specified */
for (i = 0; i < reader_count; i++) {
*reader = sc_ctx_get_reader(*ctx, i);
if (sc_detect_card_presence(*reader) & SC_READER_CARD_PRESENT) {
reader_id = i;
sc_debug(*ctx, SC_LOG_DEBUG_NORMAL, "Using the first reader"
" with a card: %s", (*reader)->name);
break;
}
}
if (reader_id >= reader_count) {
sc_debug(*ctx, SC_LOG_DEBUG_NORMAL, "No card found, using the first reader.");
reader_id = 0;
}
}
if (reader_id >= reader_count) {
sc_debug(*ctx, SC_LOG_DEBUG_NORMAL, "Invalid reader number "
"(%d), only %d available.\n", reader_id, reader_count);
return SC_ERROR_NO_READERS_FOUND;
}
*reader = sc_ctx_get_reader(*ctx, reader_id);
return SC_SUCCESS;
}
void _bin_log(sc_context_t *ctx, int type, const char *file, int line,
const char *func, const char *label, const u8 *data, size_t len,
FILE *f)
{
if (!f) {
char buf[1800];
if (data)
sc_hex_dump(ctx, SC_LOG_DEBUG_NORMAL, data, len, buf, sizeof buf);
else
buf[0] = 0;
sc_do_log(ctx, type, file, line, func,
"\n%s (%u byte%s)%s%s",
label, (unsigned int) len, len==1?"":"s", len==0?"":":\n", buf);
} else {
fprintf(f, "%s (%u byte%s)%s%s\n",
label, (unsigned int) len, len==1?"":"s", len==0?"":":\n", sc_dump_hex(data, len));
}
}
static int list_readers(sc_context_t *ctx)
{
char card_atr[0x3e];
sc_card_t *card;
sc_reader_t *reader;
size_t i, rcount = sc_ctx_get_reader_count(ctx);
if (rcount == 0) {
printf("No smart card readers found.\n");
return 0;
}
printf("%-4s %-7s %s\n", "Nr.", "Driver", "Smart Card Reader");
for (i = 0; i < rcount; i++) {
reader = sc_ctx_get_reader(ctx, i);
memset(card_atr, '\0', sizeof card_atr);
if (sc_detect_card_presence(reader) & SC_READER_CARD_PRESENT) {
if (sc_connect_card(reader, &card) == SC_SUCCESS) {
sc_bin_to_hex(card->atr.value, card->atr.len,
card_atr, sizeof card_atr, ':');
}
sc_disconnect_card(card);
} else {
strncpy(card_atr, "[no card present]", sizeof card_atr);
}
printf("%-4zu %-7s %s\n", i, reader->driver->short_name, reader->name);
printf(" ATR: %s\n", card_atr);
}
return 0;
}
int print_avail(int verbose)
{
sc_context_t *ctx = NULL;
int r;
r = sc_establish_context(&ctx, "");
if (r) {
fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
return 1;
}
ctx->debug = verbose;
ctx->flags |= SC_CTX_FLAG_ENABLE_DEFAULT_DRIVER;
r = list_readers(ctx);
sc_release_context(ctx);
return r;
}
#define ISO_READ_BINARY 0xB0
#define ISO_P1_FLAG_SFID 0x80
int read_binary_rec(sc_card_t *card, unsigned char sfid,
u8 **ef, size_t *ef_len)
{
int r;
size_t read = MAX_SM_APDU_RESP_SIZE;
sc_apdu_t apdu;
u8 *p;
if (!card || !ef || !ef_len) {
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
*ef_len = 0;
if (read > 0xff+1)
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_EXT,
ISO_READ_BINARY, ISO_P1_FLAG_SFID|sfid, 0);
else
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT,
ISO_READ_BINARY, ISO_P1_FLAG_SFID|sfid, 0);
p = realloc(*ef, read);
if (!p) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
*ef = p;
apdu.resp = *ef;
apdu.resplen = read;
apdu.le = read;
r = sc_transmit_apdu(card, &apdu);
/* emulate the behaviour of sc_read_binary */
if (r >= 0)
r = apdu.resplen;
while(1) {
if (r >= 0 && r != read) {
*ef_len += r;
break;
}
if (r < 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not read EF.");
goto err;
}
*ef_len += r;
p = realloc(*ef, *ef_len + read);
if (!p) {
r = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
*ef = p;
r = sc_read_binary(card, *ef_len,
*ef + *ef_len, read, 0);
}
r = SC_SUCCESS;
err:
return r;
}
#define ISO_WRITE_BINARY 0xD0
int write_binary_rec(sc_card_t *card, unsigned char sfid,
u8 *ef, size_t ef_len)
{
int r;
size_t write = MAX_SM_APDU_DATA_SIZE, wrote = 0;
sc_apdu_t apdu;
#ifdef ENABLE_SM
struct iso_sm_ctx *iso_sm_ctx;
#endif
if (!card) {
r = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
#ifdef ENABLE_SM
iso_sm_ctx = card->sm_ctx.info.cmd_data;
if (write > SC_MAX_APDU_BUFFER_SIZE-2
|| (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT
&& write > (((SC_MAX_APDU_BUFFER_SIZE-2
/* for encrypted APDUs we usually get authenticated status
* bytes (4B), a MAC (11B) and a cryptogram with padding
* indicator (3B without data). The cryptogram is always
* padded to the block size. */
-18) / iso_sm_ctx->block_length)
* iso_sm_ctx->block_length - 1)))
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_EXT,
ISO_WRITE_BINARY, ISO_P1_FLAG_SFID|sfid, 0);
else
#endif
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT,
ISO_WRITE_BINARY, ISO_P1_FLAG_SFID|sfid, 0);
if (write > ef_len) {
apdu.datalen = ef_len;
apdu.lc = ef_len;
} else {
apdu.datalen = write;
apdu.lc = write;
}
apdu.data = ef;
r = sc_transmit_apdu(card, &apdu);
/* emulate the behaviour of sc_write_binary */
if (r >= 0)
r = apdu.datalen;
while (1) {
if (r < 0 || r > ef_len) {
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "Could not write EF.");
goto err;
}
wrote += r;
apdu.data += r;
if (wrote >= ef_len)
break;
r = sc_write_binary(card, wrote, ef, write, 0);
}
r = SC_SUCCESS;
err:
return r;
}
int fread_to_eof(const char *file, unsigned char **buf, size_t *buflen)
{
FILE *input = NULL;
int r = 0;
unsigned char *p;
if (!buflen || !buf || !file)
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;
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (C) 2011 Frank Morgner
*
* This file is part of libnpa.
*
* libnpa is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libnpa 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* libnpa. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CCID_SSLUTIL_H
#define _CCID_SSLUTIL_H
#include <libopensc/opensc.h>
#include <openssl/err.h>
#include <libopensc/log.h>
#define ssl_error(ctx) { \
unsigned long _r; \
ERR_load_crypto_strings(); \
for (_r = ERR_get_error(); _r; _r = ERR_get_error()) { \
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, ERR_error_string(_r, NULL)); \
} \
ERR_free_strings(); \
}
#endif

View File

@@ -1,86 +0,0 @@
#! /bin/sh
CVCA=cvca.cvcert
DVCA=dvca.cvcert
TERMINAL=at.cvcert
TERM_KEY=at_key.pkcs8
ACCESS=ef_cardaccess
SECURITY=ef_cardsecurity
NPA_KEY=npa_key
if test -z "$DATA"
then
DATA=../virtualsmartcard/npa-example-data
fi
if test -z "$VICC"
then
if test -r ../virtualsmartcard/src/vpicc/vicc.in -a -n "`which python`"
then
VICC="python ../virtualsmartcard/src/vpicc/vicc.in"
elif test -x ../virtualsmartcard/src/vpicc/vicc
then
VICC=../virtualsmartcard/src/vpicc/vicc
elif test -n "`which vicc`"
then
VICC=vicc
else
echo "vicc not found"
exit 1
fi
fi
if test -z "$NPA_TOOL"
then
if test -x ../npa/src/npa-tool
then
NPA_TOOL=../npa/src/npa-tool
elif test -n "`which npa-tool`"
then
NPA_TOOL=npa-tool
else
echo "npa-tool not found"
exit 1
fi
fi
for KA in ecdh dh
do
echo "$VICC --type nPA --ef-cardsecurity=$DATA/$KA/$SECURITY \
--ef-cardaccess=$DATA/$KA/$ACCESS --ca-key=$DATA/$KA/$NPA_KEY \
--cvca=$DATA/$KA/$CVCA --disable-ta-checks >/dev/null 2>&1 &"
$VICC --type nPA --ef-cardsecurity=$DATA/$KA/$SECURITY \
--ef-cardaccess=$DATA/$KA/$ACCESS --ca-key=$DATA/$KA/$NPA_KEY \
--cvca=$DATA/$KA/$CVCA --disable-ta-checks >/dev/null 2>&1 &
VICC_PID=$!
if test $? -ne 0
then
echo "could not start vicc"
exit 1
fi
sleep 1
echo "$NPA_TOOL --pin=111111 --private-key=$DATA/$KA/$TERM_KEY \
--cv-certificate=$DATA/$KA/$DVCA,$DATA/$KA/$TERMINAL \
--older-than=19991231 --verify-validity=20201020 \
--read-dg1 --read-dg2 --read-dg3 --read-dg4 --read-dg5 --read-dg6 --read-dg7 --read-dg8 --read-dg9 --read-dg10 --read-dg11 --read-dg12 --read-dg13 --read-dg14 --read-dg15 --read-dg16 --read-dg17 --read-dg18 --read-dg19 --read-dg20 --read-dg21 \
--write-dg17=0107 --write-dg18=0108 --write-dg19=0109 --write-dg20=0200 --write-dg21=0201 \
--cvc-dir=$DATA/$KA --disable-ta-checks --disable-ca-checks"
$NPA_TOOL --pin=111111 --private-key=$DATA/$KA/$TERM_KEY \
--cv-certificate=$DATA/$KA/$DVCA,$DATA/$KA/$TERMINAL \
--older-than=19991231 --verify-validity=20201020 \
--read-dg1 --read-dg2 --read-dg3 --read-dg4 --read-dg5 --read-dg6 --read-dg7 --read-dg8 --read-dg9 --read-dg10 --read-dg11 --read-dg12 --read-dg13 --read-dg14 --read-dg15 --read-dg16 --read-dg17 --read-dg18 --read-dg19 --read-dg20 --read-dg21 \
--write-dg17=0107 --write-dg18=0108 --write-dg19=0109 --write-dg20=0200 --write-dg21=0201 \
--cvc-dir=$DATA/$KA --disable-ta-checks --disable-ca-checks
NPA_RESULT=$?
kill $VICC_PID 2>/dev/null
if test $NPA_RESULT -ne 0
then
echo "could not perform EAC"
exit 1
fi
done

View File

@@ -306,7 +306,7 @@ switches:
When |vpcd| and |vpicc| are connected you should be able to access the card When |vpcd| and |vpicc| are connected you should be able to access the card
through the PC/SC API. You can use the :command:`opensc-explorer` or through the PC/SC API. You can use the :command:`opensc-explorer` or
:command:`pcsc_scan` for testing. In @PACKAGE_NAME@'s root directory we also :command:`pcsc_scan` for testing. In @PACKAGE_NAME@'s root directory we also
provide scripts for testing with :ref:`libnpa` and PCSC-Lite's smart card provide scripts for testing with npa-tool_ and PCSC-Lite's smart card
reader driver tester. reader driver tester.
@@ -338,3 +338,4 @@ Notes and References
.. _`DevCon's source code`: https://github.com/Microsoft/Windows-driver-samples/tree/master/setup/devcon .. _`DevCon's source code`: https://github.com/Microsoft/Windows-driver-samples/tree/master/setup/devcon
.. _`Microsoft's Kernel-Mode Code Signing Walkthrough`: http://msdn.microsoft.com/en-us/library/windows/hardware/dn653569%28v=vs.85%29.aspx .. _`Microsoft's Kernel-Mode Code Signing Walkthrough`: http://msdn.microsoft.com/en-us/library/windows/hardware/dn653569%28v=vs.85%29.aspx
.. _`Windows binaries`: https://github.com/frankmorgner/vsmartcard/releases/download/virtualsmartcard-0.7/virtualsmartcard-0.7_win32.zip .. _`Windows binaries`: https://github.com/frankmorgner/vsmartcard/releases/download/virtualsmartcard-0.7/virtualsmartcard-0.7_win32.zip
.. _npa-tool: https://github.com/frankmorgner/OpenSC

View File

@@ -1 +0,0 @@
../npa/test_vicc_with_npa-tool.sh

View File

@@ -0,0 +1,86 @@
#! /bin/sh
CVCA=cvca.cvcert
DVCA=dvca.cvcert
TERMINAL=at.cvcert
TERM_KEY=at_key.pkcs8
ACCESS=ef_cardaccess
SECURITY=ef_cardsecurity
NPA_KEY=npa_key
if test -z "$DATA"
then
DATA=../virtualsmartcard/npa-example-data
fi
if test -z "$VICC"
then
if test -r ../virtualsmartcard/src/vpicc/vicc.in -a -n "`which python`"
then
VICC="python ../virtualsmartcard/src/vpicc/vicc.in"
elif test -x ../virtualsmartcard/src/vpicc/vicc
then
VICC=../virtualsmartcard/src/vpicc/vicc
elif test -n "`which vicc`"
then
VICC=vicc
else
echo "vicc not found"
exit 1
fi
fi
if test -z "$NPA_TOOL"
then
if test -x ../npa/src/npa-tool
then
NPA_TOOL=../npa/src/npa-tool
elif test -n "`which npa-tool`"
then
NPA_TOOL=npa-tool
else
echo "npa-tool not found"
exit 1
fi
fi
for KA in ecdh dh
do
echo "$VICC --type nPA --ef-cardsecurity=$DATA/$KA/$SECURITY \
--ef-cardaccess=$DATA/$KA/$ACCESS --ca-key=$DATA/$KA/$NPA_KEY \
--cvca=$DATA/$KA/$CVCA --disable-ta-checks >/dev/null 2>&1 &"
$VICC --type nPA --ef-cardsecurity=$DATA/$KA/$SECURITY \
--ef-cardaccess=$DATA/$KA/$ACCESS --ca-key=$DATA/$KA/$NPA_KEY \
--cvca=$DATA/$KA/$CVCA --disable-ta-checks >/dev/null 2>&1 &
VICC_PID=$!
if test $? -ne 0
then
echo "could not start vicc"
exit 1
fi
sleep 1
echo "$NPA_TOOL --pin=111111 --private-key=$DATA/$KA/$TERM_KEY \
--cv-certificate=$DATA/$KA/$DVCA,$DATA/$KA/$TERMINAL \
--older-than=19991231 --verify-validity=20201020 \
--read-dg1 --read-dg2 --read-dg3 --read-dg4 --read-dg5 --read-dg6 --read-dg7 --read-dg8 --read-dg9 --read-dg10 --read-dg11 --read-dg12 --read-dg13 --read-dg14 --read-dg15 --read-dg16 --read-dg17 --read-dg18 --read-dg19 --read-dg20 --read-dg21 \
--write-dg17=0107 --write-dg18=0108 --write-dg19=0109 --write-dg20=0200 --write-dg21=0201 \
--cvc-dir=$DATA/$KA --disable-ta-checks --disable-ca-checks"
$NPA_TOOL --pin=111111 --private-key=$DATA/$KA/$TERM_KEY \
--cv-certificate=$DATA/$KA/$DVCA,$DATA/$KA/$TERMINAL \
--older-than=19991231 --verify-validity=20201020 \
--read-dg1 --read-dg2 --read-dg3 --read-dg4 --read-dg5 --read-dg6 --read-dg7 --read-dg8 --read-dg9 --read-dg10 --read-dg11 --read-dg12 --read-dg13 --read-dg14 --read-dg15 --read-dg16 --read-dg17 --read-dg18 --read-dg19 --read-dg20 --read-dg21 \
--write-dg17=0107 --write-dg18=0108 --write-dg19=0109 --write-dg20=0200 --write-dg21=0201 \
--cvc-dir=$DATA/$KA --disable-ta-checks --disable-ca-checks
NPA_RESULT=$?
kill $VICC_PID 2>/dev/null
if test $NPA_RESULT -ne 0
then
echo "could not perform EAC"
exit 1
fi
done