implemented boxing commands according to TR-03119

This commit is contained in:
Frank Morgner
2013-06-03 22:07:50 +02:00
parent 93542820d2
commit 58055517ed
8 changed files with 802 additions and 115 deletions

View File

@@ -127,32 +127,3 @@ similar. Install |libnpa| and it should be recognized automatically by the
:file:`configure` script.
*****
Usage
*****
The USB CCID Emulator has various command line options to customize the appearance
on the USB host. In order to run the USB CCID Emulator GadgetFS must be loaded
and mounted. The USB CCID Emulator is compatible with the unix driver libccid_
and the `Windows USB CCID driver`_. To initialize |PACE| using the PC/SC API
you need to patch libccid (see :file:`patches`). On Windows, the USB CCID Emulator
currently has no support for |PACE|.
.. program-output:: ccid-emulator --help
.. include:: questions.txt
********************
Notes and References
********************
.. target-notes::
.. _`GadgetFS`: http://www.linux-usb.org/gadget/
.. _`OpenSC`: http://www.opensc-project.org/opensc
.. _`libccid`: http://pcsclite.alioth.debian.org/ccid.html
.. _`Windows USB CCID driver`: http://msdn.microsoft.com/en-us/windows/hardware/gg487509
.. _`OpenMoko Wiki`: http://wiki.openmoko.org/wiki/Building_Gadget_USB_Module>`_
.. [#f1] Note that the heavily outdated Windows USB CCID driver does not support secure PIN entry or PIN modification. USB CCID Emulator comes with a patch for libccid to support |PACE|, because it is not yet standardised in USB CCID. However, the traditional commands can be used without restriction.

View File

@@ -134,9 +134,16 @@ Usage
The @PACKAGE_NAME@ has various command line options to customize the appearance
on the USB host. In order to run the @PACKAGE_NAME@ GadgetFS must be loaded
and mounted. The @PACKAGE_NAME@ is compatible with the unix driver libccid_
and the `Windows USB CCID driver`_. To initialize |PACE| using the PC/SC API
you need to patch libccid (see :file:`patches`). On Windows, the @PACKAGE_NAME@
currently has no support for |PACE|.
and the `Windows USB CCID driver`_. PIN commands are supported if implemented
by the driver.
.. versionadd:: 0.7
@PACKAGE_NAME@ now supports the boxing commands defined in `BSI TR-03119
1.3`_. PIN verification/modification and |PACE| can be started by the
application transmitting (SCardTransmit) specially crafted APDUs. The
alternative initialization of |PACE| using SCardControl requires
patching the driver (available for libccid, see :file:`patches`).
.. program-output:: ccid-emulator --help
@@ -154,5 +161,6 @@ Notes and References
.. _`OpenSC`: http://www.opensc-project.org/opensc
.. _`libccid`: http://pcsclite.alioth.debian.org/ccid.html
.. _`Windows USB CCID driver`: http://msdn.microsoft.com/en-us/windows/hardware/gg487509
.. _`OpenMoko Wiki`: http://wiki.openmoko.org/wiki/Building_Gadget_USB_Module>`_
.. _`OpenMoko Wiki`: http://wiki.openmoko.org/wiki/Building_Gadget_USB_Module
.. _`BSI TR-03119`: https://www.bsi.bund.de/DE/Publikationen/TechnischeRichtlinien/tr03119/index_htm.html
.. [#f1] Note that the heavily outdated Windows USB CCID driver does not support secure PIN entry or PIN modification. @PACKAGE_NAME@ comes with a patch for libccid to support |PACE|, because it is not yet standardised in USB CCID. However, the traditional commands can be used without restriction.

View File

@@ -33,12 +33,41 @@
#include <npa/scutil.h>
#ifdef WITH_PACE
#include <npa/npa.h>
#include <npa/boxing.h>
#include <npa/iso-sm.h>
#include <npa/npa.h>
#else
int sm_stop(struct sc_card *card) { return SC_SUCCESS; }
#endif
static int
perform_PC_to_RDR_GetSlotStatus(const __u8 *in, size_t inlen, __u8 **out, size_t *outlen);
static int
perform_PC_to_RDR_IccPowerOn(const __u8 *in, size_t inlen, __u8 **out, size_t *outlen);
static int
perform_PC_to_RDR_IccPowerOff(const __u8 *in, size_t inlen, __u8 **out, size_t *outlen);
static int
perform_pseudo_apdu_EstablishPACEChannel(sc_apdu_t *apdu);
static int
perform_pseudo_apdu_GetReaderPACECapabilities(sc_apdu_t *apdu);
static int
perform_pseudo_apdu(sc_reader_t *reader, sc_apdu_t *apdu);
static int
perform_PC_to_RDR_XfrBlock(const u8 *in, size_t inlen, __u8** out, size_t *outlen);
static int
perform_PC_to_RDR_GetParamters(const __u8 *in, size_t inlen, __u8** out, size_t *outlen);
static int
perform_PC_to_RDR_Secure_EstablishPACEChannel(sc_card_t *card,
const __u8 *abData, size_t abDatalen,
__u8 **abDataOut, size_t *abDataOutLen);
static int
perform_PC_to_RDR_Secure_GetReadersPACECapabilities(__u8 **abDataOut,
size_t *abDataOutLen);
static int
perform_PC_to_RDR_Secure(const __u8 *in, size_t inlen, __u8** out, size_t *outlen);
static int
perform_unknown(const __u8 *in, size_t inlen, __u8 **out, size_t *outlen);
static sc_context_t *ctx = NULL;
static sc_card_t *card = NULL;
static sc_reader_t *reader = NULL;
@@ -153,21 +182,24 @@ static int get_rapdu(sc_apdu_t *apdu, __u8 **buf, size_t *resplen)
{
int sc_result;
if (!apdu || !buf || !resplen) {
if (!apdu || !buf || !resplen || !card) {
sc_result = SC_ERROR_INVALID_ARGUMENTS;
goto err;
}
apdu->resplen = apdu->le;
/* Get two more bytes to later use as return buffer including sw1 and sw2 */
apdu->resp = realloc(*buf, apdu->resplen + sizeof(__u8) + sizeof(__u8));
apdu->resp = realloc(*buf, apdu->resplen);
if (!apdu->resp) {
sc_result = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
*buf = apdu->resp;
sc_result = sc_transmit_apdu(card, apdu);
if (apdu->cla == 0XFF) {
sc_result = perform_pseudo_apdu(card->reader, apdu);
} else {
sc_result = sc_transmit_apdu(card, apdu);
}
if (sc_result < 0) {
goto err;
}
@@ -178,10 +210,15 @@ static int get_rapdu(sc_apdu_t *apdu, __u8 **buf, size_t *resplen)
goto err;
}
apdu->resp[apdu->resplen] = apdu->sw1;
apdu->resp[apdu->resplen + sizeof(__u8)] = apdu->sw2;
/* Get two more bytes to use as return buffer including sw1 and sw2 */
*buf = realloc(apdu->resp, apdu->resplen + sizeof(__u8) + sizeof(__u8));
if (!*buf) {
sc_result = SC_ERROR_OUT_OF_MEMORY;
goto err;
}
*buf = apdu->resp;
(*buf)[apdu->resplen] = apdu->sw1;
(*buf)[apdu->resplen + sizeof(__u8)] = apdu->sw2;
*resplen = apdu->resplen + sizeof(__u8) + sizeof(__u8);
sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "R-APDU, %d byte%s:\tsw1=%02x sw2=%02x",
@@ -387,6 +424,9 @@ perform_PC_to_RDR_IccPowerOn(const __u8 *in, size_t inlen, __u8 **out, size_t *o
}
if (sc_result >= 0) {
#ifndef DISABLE_GLOBAL_BOXING_INITIALIZATION
sc_initialize_boxing_cmds(ctx);
#endif
return get_RDR_to_PC_SlotStatus(request->bSeq,
sc_result, out, outlen, card->atr.value, card->atr.len);
} else {
@@ -422,75 +462,163 @@ perform_PC_to_RDR_IccPowerOff(const __u8 *in, size_t inlen, __u8 **out, size_t *
out, outlen, NULL, 0);
}
static int
perform_pseudo_apdu(const __u8 *in, size_t inlen, __u8 **out, size_t *outlen)
{
__u8 *p;
sc_apdu_t apdu;
struct sw {
unsigned char sw1;
unsigned char sw2;
};
static const struct sw iso_sw_ok = { 0x90, 0x00};
static const struct sw iso_sw_incorrect_p1_p2 = { 0x6A, 0x86};
static const struct sw iso_sw_ref_data_not_found = {0x6A, 0x88};
static const struct sw iso_sw_inconsistent_data = {0x6A, 0x87};
static const struct sw iso_sw_func_not_supported = {0x6A, 0x81};
static const struct sw iso_sw_ins_not_supported = {0x6D, 0x00};
if (!in || !out || !outlen)
static int
perform_pseudo_apdu_EstablishPACEChannel(sc_apdu_t *apdu)
{
struct establish_pace_channel_input pace_input;
struct establish_pace_channel_output pace_output;
int r;
memset(&pace_input, 0, sizeof pace_input);
memset(&pace_output, 0, sizeof pace_output);
r = boxing_buf_to_pace_input(reader->ctx, apdu->data, apdu->datalen,
&pace_input);
if (r < 0)
goto err;
r = perform_pace(card, pace_input, &pace_output,
EAC_TR_VERSION_2_02);
if (r < 0)
goto err;
r = boxing_pace_output_to_buf(reader->ctx, &pace_output, &apdu->resp,
&apdu->resplen);
err:
free((unsigned char *) pace_input.chat);
free((unsigned char *) pace_input.certificate_description);
free((unsigned char *) pace_input.pin);
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);
return r;
}
static int
perform_pseudo_apdu_GetReaderPACECapabilities(sc_apdu_t *apdu)
{
unsigned long sc_reader_t_capabilities = SC_READER_CAP_PACE_GENERIC
| SC_READER_CAP_PACE_EID | SC_READER_CAP_PACE_ESIGN;
return boxing_pace_capabilities_to_buf(reader->ctx,
sc_reader_t_capabilities, &apdu->resp, &apdu->resplen);
}
#define min(a,b) (a<b?a:b)
static int
perform_pseudo_apdu(sc_reader_t *reader, sc_apdu_t *apdu)
{
if (!reader || !apdu || apdu->cla != 0xff)
return SC_ERROR_INVALID_ARGUMENTS;
if (*in != 0xFF)
sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "malformed PC_to_RDR_XfrBlock, will continue anyway");
LOG_TEST_RET(ctx, sc_bytes2apdu(ctx, in, inlen, &apdu), "Could not parse Pseudo-APDU");
switch (apdu.ins) {
switch (apdu->ins) {
case 0x9A:
/* GetReaderInfo */
if (in[2] != 0x01 || inlen != 5 || in[4] != 0x00) {
parse_error:
p = realloc(*out, 2);
if (!p)
SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_OUT_OF_MEMORY);
*out = p;
memcpy(*out, "\x6A\x86", 2);
*outlen = 2;
}
/* TODO Merge this with STRINGID_MFGR, STRINGID_PRODUCT in usb.c */
/* Copied from olsc/AusweisApp/Data/siqTerminalsInfo.cfg */
const char *info;
const char *Herstellername = "REINER SCT";
const char *Produktname = "cyberJack RFID komfort";
const char *Firmwareversion = "1.0";
const char *Treiberversion = "3.99.5";
switch (in[3]) {
case 0x01:
info = Herstellername;
break;
case 0x03:
info = Produktname;
break;
case 0x06:
info = Firmwareversion;
break;
case 0x07:
info = Treiberversion;
break;
default:
goto parse_error;
}
size_t infolen = strlen(info);
p = realloc(*out, infolen+2);
if (!p)
SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_OUT_OF_MEMORY);
*out = p;
memcpy(*out, info, infolen);
*out[infolen] = 0x90;
*out[infolen+1] = 0x00;
*outlen = infolen+2;
break;
switch (apdu->p1) {
case 0x01:
/* GetReaderInfo */
if (apdu->datalen != 0) {
apdu->resplen = 0;
apdu->sw1 = iso_sw_incorrect_p1_p2.sw1;
apdu->sw2 = iso_sw_incorrect_p1_p2.sw2;
goto err;
}
/* TODO Merge this with STRINGID_MFGR, STRINGID_PRODUCT in usb.c */
/* Copied from olsc/AusweisApp/Data/siqTerminalsInfo.cfg */
char *Herstellername = "REINER SCT";
char *Produktname = "cyberJack RFID komfort";
char *Firmwareversion = "1.0";
char *Treiberversion = "3.99.5";
switch (apdu->p2) {
case 0x01:
apdu->resplen = min(apdu->resplen, strlen(Herstellername));
memcpy(apdu->resp, Herstellername, apdu->resplen);
break;
case 0x03:
apdu->resplen = min(apdu->resplen, strlen(Produktname));
memcpy(apdu->resp, Produktname, apdu->resplen);
break;
case 0x06:
apdu->resplen = min(apdu->resplen, strlen(Firmwareversion));
memcpy(apdu->resp, Firmwareversion, apdu->resplen);
break;
case 0x07:
apdu->resplen = min(apdu->resplen, strlen(Treiberversion));
memcpy(apdu->resp, Treiberversion, apdu->resplen);
break;
default:
apdu->resplen = 0;
apdu->sw1 = iso_sw_ref_data_not_found.sw1;
apdu->sw2 = iso_sw_ref_data_not_found.sw2;
goto err;
}
break;
case 0x04:
switch (apdu->p2) {
case 0x04:
/* VerifyPIN/ModifyPIN */
LOG_TEST_RET(ctx,
perform_PC_to_RDR_Secure(apdu->data, apdu->datalen, &apdu->resp, &apdu->resplen),
"Could not perform PC_to_RDR_Secure");
apdu->sw1 = iso_sw_ok.sw1;
apdu->sw2 = iso_sw_ok.sw2;
break;
case 0x01:
/* GetReaderPACECapabilities */
LOG_TEST_RET(ctx,
perform_pseudo_apdu_GetReaderPACECapabilities(apdu),
"Could not get reader's PACE Capabilities");
apdu->sw1 = iso_sw_ok.sw1;
apdu->sw2 = iso_sw_ok.sw2;
break;
case 0x02:
/* EstablishPACEChannel */
LOG_TEST_RET(ctx,
perform_pseudo_apdu_EstablishPACEChannel(apdu),
"Could not perform PACE");
apdu->sw1 = iso_sw_ok.sw1;
apdu->sw2 = iso_sw_ok.sw2;
break;
case 0x03:
/* DestroyPACEChannel */
default:
apdu->sw1 = iso_sw_func_not_supported.sw1;
apdu->sw2 = iso_sw_func_not_supported.sw2;
goto err;
}
break;
default:
apdu->sw1 = iso_sw_func_not_supported.sw1;
apdu->sw2 = iso_sw_func_not_supported.sw2;
goto err;
}
break;
default:
p = realloc(*out, 2);
if (!p)
SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_OUT_OF_MEMORY);
*out = p;
memcpy(*out, "\x6A\x81", 2);
*outlen = 2;
apdu->sw1 = iso_sw_ins_not_supported.sw1;
apdu->sw2 = iso_sw_ins_not_supported.sw2;
goto err;
}
err:
return SC_SUCCESS;
}
@@ -521,18 +649,13 @@ perform_PC_to_RDR_XfrBlock(const u8 *in, size_t inlen, __u8** out, size_t *outle
SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_DATA);
}
if (apdulen >= 1 && *abDataIn == 0xff) {
sc_result = perform_pseudo_apdu(abDataIn, apdulen, &abDataOut,
&abDataOutLen);
} else {
sc_result = sc_bytes2apdu(ctx, abDataIn, apdulen, &apdu);
if (sc_result >= 0)
sc_result = get_rapdu(&apdu, &abDataOut,
&abDataOutLen);
else
bin_log(ctx, SC_LOG_DEBUG_VERBOSE, "Invalid APDU", abDataIn,
__le32_to_cpu(request->dwLength));
}
sc_result = sc_bytes2apdu(ctx, abDataIn, apdulen, &apdu);
if (sc_result >= 0)
sc_result = get_rapdu(&apdu, &abDataOut,
&abDataOutLen);
else
bin_log(ctx, SC_LOG_DEBUG_VERBOSE, "Invalid APDU", abDataIn,
__le32_to_cpu(request->dwLength));
sc_result = get_RDR_to_PC_DataBlock(request->bSeq, sc_result,
out, outlen, abDataOut, abDataOutLen);

View File

@@ -16,7 +16,7 @@ MAINTAINERCLEANFILES = $(BUILT_SOURCES) npa-tool.ggo $(dist_man1_MANS)
dist_man1_MANS = npa-tool.1
libnpa_la_SOURCES = iso-sm.c scutil.c npa.c
libnpa_la_SOURCES = iso-sm.c scutil.c npa.c boxing.c
libnpa_la_LIBADD = $(OPENSSL_LIBS) $(OPENPACE_LIBS) $(OPENSC_LIBS)
libnpa_la_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS)
libnpa_la_LDFLAGS = -no-undefined
@@ -58,4 +58,5 @@ noinst_HEADERS = \
nobase_include_HEADERS = \
npa/iso-sm.h \
npa/npa.h \
npa/boxing.h \
npa/scutil.h

509
npa/src/boxing.c Normal file
View File

@@ -0,0 +1,509 @@
/*
* boxing.c: implementation related to boxing commands with pseudo APDUs
*
* Copyright (C) 2013 Frank Morgner <morgner@informatik.hu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <libopensc/asn1.h>
#include <libopensc/log.h>
#include <libopensc/opensc.h>
#include <libopensc/pace.h>
#include <stdlib.h>
#include <string.h>
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;
static const struct sc_asn1_entry g_EstablishPACEChannelInput_data[] = {
{ "passwordID",
/* use an OCTET STRING to avoid a conversion to int */
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x01, 0, NULL, NULL },
{ "transmittedPassword",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x02, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
{ "cHAT",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x03, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
{ "certificateDescription",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x04, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
{ "hashOID",
/* use an OCTET STRING to avoid a conversion to struct sc_object_id */
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x05, 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_OCTET_STRING, SC_ASN1_CTX|0x01, 0, NULL, NULL },
{ "statusMSESetAT",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x02, 0, NULL, NULL },
{ "efCardAccess",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x03, SC_ASN1_ALLOC, NULL, NULL },
{ "idPICC",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x04, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
{ "curCAR",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x05, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
{ "prevCAR",
SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x06, 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)
{
const size_t count_data =
sizeof g_EstablishPACEChannelInput_data/
sizeof *g_EstablishPACEChannelInput_data;
const size_t count_sequence =
sizeof g_EstablishPACEChannel/
sizeof *g_EstablishPACEChannel;
size_t pin_id_len = sizeof input->pin_id, i;
struct sc_asn1_entry EstablishPACEChannelInput_data[count_data];
struct sc_asn1_entry EstablishPACEChannel[count_sequence];
for (i = 0; i < count_sequence; i++)
sc_copy_asn1_entry(g_EstablishPACEChannel+i,
EstablishPACEChannel+i);
sc_format_asn1_entry(EstablishPACEChannel,
EstablishPACEChannelInput_data, 0, 1);
for (i = 0; i < count_data; i++)
sc_copy_asn1_entry(g_EstablishPACEChannelInput_data+i,
EstablishPACEChannelInput_data+i);
sc_format_asn1_entry(EstablishPACEChannelInput_data,
(unsigned char *) &input->pin_id, &pin_id_len, 1);
if (input->pin)
sc_format_asn1_entry(EstablishPACEChannelInput_data,
(unsigned char *) input->pin,
(size_t *) &input->pin_length, 1);
if (input->chat)
sc_format_asn1_entry(EstablishPACEChannelInput_data,
(unsigned char *) input->chat,
(size_t *) &input->chat_length, 1);
if (input->certificate_description)
sc_format_asn1_entry(EstablishPACEChannelInput_data,
(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)
{
const size_t count_data =
sizeof g_EstablishPACEChannelInput_data/
sizeof *g_EstablishPACEChannelInput_data;
const size_t count_sequence =
sizeof g_EstablishPACEChannel/
sizeof *g_EstablishPACEChannel;
size_t pin_id_len = sizeof input->pin_id, i;
struct sc_asn1_entry EstablishPACEChannelInput_data[count_data];
struct sc_asn1_entry EstablishPACEChannel[count_sequence];
for (i = 0; i < count_sequence; i++)
sc_copy_asn1_entry(g_EstablishPACEChannel+i,
EstablishPACEChannel+i);
sc_format_asn1_entry(EstablishPACEChannel,
EstablishPACEChannelInput_data, 0, 0);
for (i = 0; i < count_data; i++)
sc_copy_asn1_entry(g_EstablishPACEChannelInput_data+i,
EstablishPACEChannelInput_data+i);
sc_format_asn1_entry(EstablishPACEChannelInput_data+0,
&input->pin_id, &pin_id_len, 0);
sc_format_asn1_entry(EstablishPACEChannelInput_data+1,
(unsigned char *) input->pin, &input->pin_length, 0);
sc_format_asn1_entry(EstablishPACEChannelInput_data+2,
(unsigned char *) input->chat, &input->chat_length, 0);
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)
{
const size_t count_data =
sizeof g_EstablishPACEChannelOutput_data/
sizeof *g_EstablishPACEChannelOutput_data;
const size_t count_sequence =
sizeof g_EstablishPACEChannel/
sizeof *g_EstablishPACEChannel;
uint16_t status_mse_set_at = ((output->mse_set_at_sw1 & 0xff) << 8) | output->mse_set_at_sw2;
size_t i, result_len = sizeof output->result,
status_mse_set_at_len = sizeof status_mse_set_at;
struct sc_asn1_entry EstablishPACEChannelOutput_data[count_data];
struct sc_asn1_entry EstablishPACEChannel[count_sequence];
for (i = 0; i < count_sequence; i++)
sc_copy_asn1_entry(g_EstablishPACEChannel+i,
EstablishPACEChannel+i);
sc_format_asn1_entry(EstablishPACEChannel,
EstablishPACEChannelOutput_data, 0, 1);
for (i = 0; i < count_data; i++)
sc_copy_asn1_entry(g_EstablishPACEChannelOutput_data+i,
EstablishPACEChannelOutput_data+i);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+0,
(unsigned char *) &output->result, &result_len, 1);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+1,
&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,
output->id_icc, (size_t *) &output->id_icc_length, 1);
if (output->recent_car)
sc_format_asn1_entry(EstablishPACEChannelOutput_data+4,
output->recent_car, (size_t *) &output->recent_car_length, 1);
if (output->previous_car)
sc_format_asn1_entry(EstablishPACEChannelOutput_data+5,
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)
{
const size_t count_data =
sizeof g_EstablishPACEChannelOutput_data/
sizeof *g_EstablishPACEChannelOutput_data;
const size_t count_sequence =
sizeof g_EstablishPACEChannel/
sizeof *g_EstablishPACEChannel;
uint16_t status_mse_set_at;
size_t i, result_len = sizeof output->result,
status_mse_set_at_len = sizeof status_mse_set_at;
struct sc_asn1_entry EstablishPACEChannelOutput_data[count_data];
struct sc_asn1_entry EstablishPACEChannel[count_sequence];
for (i = 0; i < count_sequence; i++)
sc_copy_asn1_entry(g_EstablishPACEChannel+i,
EstablishPACEChannel+i);
sc_format_asn1_entry(EstablishPACEChannel,
EstablishPACEChannelOutput_data, 0, 0);
for (i = 0; i < count_data; i++)
sc_copy_asn1_entry(g_EstablishPACEChannelOutput_data+i,
EstablishPACEChannelOutput_data+i);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+0,
&output->result, &result_len, 0);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+1,
&status_mse_set_at, &status_mse_set_at_len, 0);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+2,
output->ef_cardaccess, &output->ef_cardaccess_length, 0);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+3,
output->id_icc, &output->id_icc_length, 0);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+4,
output->recent_car, &output->recent_car_length, 0);
sc_format_asn1_entry(EstablishPACEChannelOutput_data+5,
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;
}
static int boxing_perform_verify(struct sc_reader *reader, struct sc_pin_cmd_data *data)
{
return SC_ERROR_NOT_SUPPORTED;
}
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_BOOLEAN, SC_ASN1_CTX|0x01, SC_ASN1_ALLOC, NULL, NULL },
{ "capabilityEID",
SC_ASN1_BOOLEAN, SC_ASN1_CTX|0x02, SC_ASN1_ALLOC, NULL, NULL },
{ "capabilityESign",
SC_ASN1_BOOLEAN, SC_ASN1_CTX|0x03, SC_ASN1_ALLOC, NULL, NULL },
{ "capabilityDestroy",
SC_ASN1_BOOLEAN, SC_ASN1_CTX|0x04, 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)
{
const size_t count_data =
sizeof g_PACECapabilities_data/
sizeof *g_PACECapabilities_data;
const size_t count_sequence =
sizeof g_PACECapabilities/
sizeof *g_PACECapabilities;
int capabilityPACE = 0, capabilityEID = 0, capabilityESign = 0,
capabilityDestroy = 0;
size_t i;
struct sc_asn1_entry PACECapabilities_data[count_data];
struct sc_asn1_entry PACECapabilities[count_sequence];
for (i = 0; i < count_sequence; i++)
sc_copy_asn1_entry(g_PACECapabilities+i,
PACECapabilities+i);
sc_format_asn1_entry(PACECapabilities,
PACECapabilities_data, 0, 1);
for (i = 0; i < count_data; i++)
sc_copy_asn1_entry(g_PACECapabilities_data+i,
PACECapabilities_data+i);
sc_format_asn1_entry(PACECapabilities_data+0,
&capabilityPACE, NULL, 0);
sc_format_asn1_entry(PACECapabilities_data+1,
&capabilityEID, NULL, 0);
sc_format_asn1_entry(PACECapabilities_data+2,
&capabilityESign, NULL, 0);
sc_format_asn1_entry(PACECapabilities_data+3,
&capabilityDestroy, 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 (capabilityPACE)
*sc_reader_t_capabilities |= SC_READER_CAP_PACE_GENERIC;
if (capabilityEID)
*sc_reader_t_capabilities |= SC_READER_CAP_PACE_EID;
if (capabilityESign)
*sc_reader_t_capabilities |= SC_READER_CAP_PACE_ESIGN;
if (capabilityDestroy)
*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)
{
const size_t count_data =
sizeof g_PACECapabilities_data/
sizeof *g_PACECapabilities_data;
const size_t count_sequence =
sizeof g_PACECapabilities/
sizeof *g_PACECapabilities;
int yes = 1, no = 0;
size_t i;
struct sc_asn1_entry PACECapabilities_data[count_data];
struct sc_asn1_entry PACECapabilities[count_sequence];
for (i = 0; i < count_sequence; i++)
sc_copy_asn1_entry(g_EstablishPACEChannel+i,
PACECapabilities+i);
sc_format_asn1_entry(PACECapabilities,
PACECapabilities_data, 0, 1);
for (i = 0; i < count_data; i++)
sc_copy_asn1_entry(g_PACECapabilities_data+i,
PACECapabilities_data+i);
if (sc_reader_t_capabilities & SC_READER_CAP_PACE_GENERIC)
sc_format_asn1_entry(PACECapabilities_data+0,
&yes, NULL, 1);
else
sc_format_asn1_entry(PACECapabilities_data+0,
&no, NULL, 1);
if (sc_reader_t_capabilities & SC_READER_CAP_PACE_EID)
sc_format_asn1_entry(PACECapabilities_data+1,
&yes, NULL, 1);
else
sc_format_asn1_entry(PACECapabilities_data+1,
&no, NULL, 1);
if (sc_reader_t_capabilities & SC_READER_CAP_PACE_ESIGN)
sc_format_asn1_entry(PACECapabilities_data+2,
&yes, NULL, 1);
else
sc_format_asn1_entry(PACECapabilities_data+2,
&no, NULL, 1);
if (sc_reader_t_capabilities & SC_READER_CAP_PACE_DESTROY_CHANNEL)
sc_format_asn1_entry(PACECapabilities_data+3,
&yes, NULL, 1);
else
sc_format_asn1_entry(PACECapabilities_data+3,
&no, NULL, 1);
return sc_asn1_encode(ctx, PACECapabilities, asn1, asn1_len);
}
#ifdef 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) {
sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
"%s does not support boxing commands", reader->name);
} 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;
}
}
#ifdef DISABLE_GLOBAL_BOXING_INITIALIZATION
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);
}
}
#endif

View File

@@ -24,6 +24,7 @@
#include <eac/pace.h>
#include <libopensc/log.h>
#include <libopensc/opensc.h>
#include <npa/boxing.h>
#include <npa/iso-sm.h>
#include <npa/npa.h>
#include <npa/scutil.h>
@@ -376,6 +377,10 @@ main (int argc, char **argv)
exit(1);
}
#ifndef DISABLE_GLOBAL_BOXING_INITIALIZATION
sc_initialize_boxing_cmds(ctx);
#endif
if (cmdline.break_flag) {
/* The biggest buffer sprintf could write with "%llu" */
char secretbuf[strlen("18446744073709551615")+1];

View File

@@ -31,6 +31,7 @@
#include <libopensc/opensc.h>
#include <npa/iso-sm.h>
#include <npa/npa.h>
#include <npa/boxing.h>
#include <npa/scutil.h>
#include <openssl/asn1t.h>
#include <openssl/bio.h>
@@ -1272,6 +1273,10 @@ static int establish_pace_channel(sc_card_t *card,
}
}
#ifdef DISABLE_GLOBAL_BOXING_INITIALIZATION
sc_detect_boxing_cmds(card->reader);
#endif
if (card->reader->capabilities & SC_READER_CAP_PACE_GENERIC
&& card->reader->ops->perform_pace) {
r = card->reader->ops->perform_pace(card->reader, &pace_input, pace_output);

65
npa/src/npa/boxing.h Normal file
View File

@@ -0,0 +1,65 @@
/*
* boxing.c: interface related to boxing commands with pseudo APDUs
*
* Copyright (C) 2013 Frank Morgner <morgner@informatik.hu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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 */
/* currently disabled, because the OpenSC PC/SC reader implementation
* allows a transmit only when a card is connected. */
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