SM: initial implementation of secure messaging framework

This commit is contained in:
Viktor Tarasov 2012-06-04 09:26:56 +02:00
parent 2078654d2b
commit cfd5aaba7d
9 changed files with 1095 additions and 279 deletions

View File

@ -74,6 +74,16 @@ case "${host}" in
;;
esac
case "${host}" in
*-mingw*|*-winnt*|*-cygwin*)
DEBUG_FILE="%TEMP%\opensc-debug.log"
;;
*)
DEBUG_FILE="/tmp/opensc-debug.log"
;;
esac
AC_DEFINE_UNQUOTED([DEBUG_FILE], ["${DEBUG_FILE}"], [Debug file])
AC_ARG_ENABLE(
[strict],
[AS_HELP_STRING([--enable-strict],[enable strict compile mode @<:@disabled@:>@])],
@ -137,6 +147,13 @@ AC_ARG_ENABLE(
[enable_minidriver="no"]
)
AC_ARG_ENABLE(
[sm],
[AS_HELP_STRING([--enable-sm],[enable secure messaging support and modules @<:@disabled@:>@])],
,
[enable_sm="no"]
)
AC_ARG_ENABLE(
[man],
[AS_HELP_STRING([--disable-man],[disable installation of manuals @<:@enabled for none Windows@:>@])],
@ -298,6 +315,20 @@ if test "${enable_minidriver}" = "yes"; then
AC_DEFINE([ENABLE_MINIDRIVER], [1], [Enable minidriver support])
fi
if test "${enable_sm}" = "yes"; then
AC_DEFINE([ENABLE_SM], [1], [Enable secure messaging support])
case "${host}" in
*-mingw*|*-winnt*|*-cygwin*)
DEFAULT_SM_MODULE="smm-local.dll"
;;
*)
DEFAULT_SM_MODULE="libsmm-local.so.3"
;;
esac
AC_DEFINE_UNQUOTED([DEFAULT_SM_MODULE], ["${DEFAULT_SM_MODULE}"], [Default SM module])
fi
AC_ARG_VAR([ZLIB_CFLAGS], [C compiler flags for zlib])
AC_ARG_VAR([ZLIB_LIBS], [linker flags for zlib])
if test -z "${ZLIB_LIBS}"; then
@ -547,6 +578,8 @@ AC_SUBST([OPTIONAL_OPENCT_CFLAGS])
AC_SUBST([OPTIONAL_OPENCT_LIBS])
AC_SUBST([OPTIONAL_PCSC_CFLAGS])
AC_SUBST([LIBRARY_BITNESS])
AC_SUBST([DEFAULT_SM_MODULE])
AC_SUBST([DEBUG_FILE])
AM_CONDITIONAL([ENABLE_MAN], [test "${enable_man}" = "yes"])
AM_CONDITIONAL([ENABLE_ZLIB], [test "${enable_zlib}" = "yes"])
@ -557,6 +590,7 @@ AM_CONDITIONAL([ENABLE_DOC], [test "${enable_doc}" = "yes"])
AM_CONDITIONAL([WIN32], [test "${WIN32}" = "yes"])
AM_CONDITIONAL([CYGWIN], [test "${CYGWIN}" = "yes"])
AM_CONDITIONAL([ENABLE_MINIDRIVER], [test "${enable_minidriver}" = "yes"])
AM_CONDITIONAL([ENABLE_SM], [test "${enable_sm}" = "yes"])
if test "${enable_pedantic}" = "yes"; then
enable_strict="yes";
@ -585,6 +619,7 @@ AC_CONFIG_FILES([
src/tests/Makefile
src/tests/regression/Makefile
src/tools/Makefile
src/sm/Makefile
src/minidriver/Makefile
src/minidriver/opensc-minidriver.inf
win32/Makefile
@ -618,6 +653,9 @@ PC/SC support: ${enable_pcsc}
OpenCT support: ${enable_openct}
CT-API support: ${enable_ctapi}
minidriver support: ${enable_minidriver}
SM support: ${enable_sm}
SM default module: ${DEFAULT_SM_MODULE}
Debug file: ${DEBUG_FILE}
PC/SC default provider: ${DEFAULT_PCSC_PROVIDER}

View File

@ -1,5 +1,5 @@
MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
DISTCLEANFILES = opensc.conf
DISTCLEANFILES = opensc.conf
SUFFIXES = .in
@ -16,6 +16,8 @@ opensc.conf: opensc.conf.in force
sed \
-e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \
-e 's|@DEFAULT_PCSC_PROVIDER[@]|$(DEFAULT_PCSC_PROVIDER)|g' \
-e 's|@DEFAULT_SM_MODULE[@]|$(DEFAULT_SM_MODULE)|g' \
-e 's|@DEBUG_FILE[@]|$(DEBUG_FILE)|g' \
< $< > $@
install-exec-hook: opensc.conf

View File

@ -10,7 +10,7 @@ noinst_HEADERS = cards.h ctbcs.h internal.h esteid.h muscle.h muscle-filesystem.
opensc.h pkcs15.h \
cardctl.h asn1.h log.h \
errors.h types.h compression.h itacns.h iso7816.h \
authentic.h iasecc.h iasecc-sdo.h
authentic.h iasecc.h iasecc-sdo.h sm.h
AM_CPPFLAGS = -DOPENSC_CONF_PATH=\"$(sysconfdir)/opensc.conf\"
AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_OPENCT_CFLAGS) \

View File

@ -26,6 +26,7 @@
#include <string.h>
#include "internal.h"
#include "asn1.h"
/*********************************************************************/
/* low level APDU handling functions */
@ -73,7 +74,7 @@ static size_t sc_apdu_get_length(const sc_apdu_t *apdu, unsigned int proto)
* @param ctx sc_context_t object (used for logging)
* @param apdu APDU to be encoded as an octet string
* @param proto protocol version to be used
* @param out output buffer of size outlen.
* @param out output buffer of size outlen.
* @param outlen size of hte output buffer
* @return SC_SUCCESS on success and an error code otherwise
*/
@ -121,14 +122,13 @@ static int sc_apdu2bytes(sc_context_t *ctx, const sc_apdu_t *apdu,
/* in case of T0 the command is transmitted in chunks
* < 255 using the ENVELOPE command ... */
if (apdu->lc > 255) {
/* ... so if Lc is greater than 255 bytes
/* ... so if Lc is greater than 255 bytes
* an error has occurred on a higher level */
sc_debug(ctx, SC_LOG_DEBUG_NORMAL,
"invalid Lc length for CASE 3 "
"extended APDU (need ENVELOPE)");
sc_log(ctx, "invalid Lc length for CASE 3 extended APDU (need ENVELOPE)");
return SC_ERROR_INVALID_ARGUMENTS;
}
} else {
}
else {
/* in case of T1 always use 3 bytes for length */
*p++ = (u8)0x00;
*p++ = (u8)(apdu->lc >> 8);
@ -151,7 +151,8 @@ static int sc_apdu2bytes(sc_context_t *ctx, const sc_apdu_t *apdu,
* transferred using ENVELOPE and GET RESPONSE */
*p++ = (u8)apdu->lc;
memcpy(p, apdu->data, apdu->lc);
} else {
}
else {
*p++ = (u8)0x00;
*p++ = (u8)(apdu->lc >> 8);
*p++ = (u8)apdu->lc;
@ -215,7 +216,7 @@ int sc_apdu_set_resp(sc_context_t *ctx, sc_apdu_t *apdu, const u8 *buf,
{
if (len < 2) {
/* no SW1 SW2 ... something went terrible wrong */
sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "invalid response: SW1 SW2 missing");
sc_log(ctx, "invalid response: SW1 SW2 missing");
return SC_ERROR_INTERNAL;
}
/* set the SW1 and SW2 status bytes (the last two bytes of
@ -233,6 +234,96 @@ int sc_apdu_set_resp(sc_context_t *ctx, sc_apdu_t *apdu, const u8 *buf,
return SC_SUCCESS;
}
#ifdef ENABLE_SM
static const struct sc_asn1_entry c_asn1_sm_response[4] = {
{ "encryptedData", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 7, SC_ASN1_OPTIONAL, NULL, NULL },
{ "statusWord", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 0x19, 0, NULL, NULL },
{ "mac", SC_ASN1_OCTET_STRING, SC_ASN1_CTX | 0x0E, 0, NULL, NULL },
{ NULL, 0, 0, 0, NULL, NULL }
};
static int
sc_sm_parse_answer(struct sc_context *ctx, unsigned char *resp_data, size_t resp_len,
struct sm_card_response *out)
{
struct sc_asn1_entry asn1_sm_response[4];
unsigned char data[SC_MAX_APDU_BUFFER_SIZE];
size_t data_len = sizeof(data);
unsigned char status[2] = {0, 0};
size_t status_len = sizeof(status);
unsigned char mac[8];
size_t mac_len = sizeof(mac);
int r;
if (!resp_data || !resp_len || !out)
return SC_ERROR_INVALID_ARGUMENTS;
sc_copy_asn1_entry(c_asn1_sm_response, asn1_sm_response);
sc_format_asn1_entry(asn1_sm_response + 0, data, &data_len, 0);
sc_format_asn1_entry(asn1_sm_response + 1, status, &status_len, 0);
sc_format_asn1_entry(asn1_sm_response + 2, mac, &mac_len, 0);
r = sc_asn1_decode(ctx, asn1_sm_response, resp_data, resp_len, NULL, NULL);
if (r)
return r;
if (asn1_sm_response[1].flags & SC_ASN1_PRESENT) {
out->sw1 = status[0];
out->sw2 = status[1];
}
if (asn1_sm_response[2].flags & SC_ASN1_PRESENT) {
memcpy(out->mac, mac, mac_len);
out->mac_len = mac_len;
}
/* TODO: to be continued ... */
return SC_SUCCESS;
}
/** parse answer of SM protected APDU returned by APDU or by 'GET RESPONSE'
* @param card 'sc_card' smartcard object
* @param resp_data 'raw data returned by SM protected APDU
* @param resp_len 'length of raw data returned by SM protected APDU
* @param ref_rv 'status word returned by APDU or 'GET RESPONSE' (can be different from status word encoded into SM response date)
* @param apdu 'sc_apdu' object to update
* @return SC_SUCCESS on success and an error code otherwise
*/
static int
sc_sm_update_apdu_response(struct sc_card *card, unsigned char *resp_data, size_t resp_len, int ref_rv,
struct sc_apdu *apdu)
{
struct sm_card_response sm_resp;
int r;
if (!apdu)
return SC_ERROR_INVALID_ARGUMENTS;
else if (!resp_data || !resp_len)
return SC_SUCCESS;
memset(&sm_resp, 0, sizeof(sm_resp));
r = sc_sm_parse_answer(card->ctx, resp_data, resp_len, &sm_resp);
if (r)
return r;
else if (!sm_resp.sw1 && !sm_resp.sw2)
return SC_ERROR_INVALID_DATA;
else if (ref_rv != sc_check_sw(card, sm_resp.sw1, sm_resp.sw2))
return SC_ERROR_INVALID_DATA;
if (sm_resp.mac_len) {
if (sm_resp.mac_len > sizeof(apdu->mac))
return SC_ERROR_INVALID_DATA;
memcpy(apdu->mac, sm_resp.mac, sm_resp.mac_len);
apdu->mac_len = sm_resp.mac_len;
}
apdu->sw1 = sm_resp.sw1;
apdu->sw2 = sm_resp.sw2;
return SC_SUCCESS;
}
#endif
/*********************************************************************/
/* higher level APDU transfer handling functions */
/*********************************************************************/
@ -247,7 +338,7 @@ int sc_apdu_set_resp(sc_context_t *ctx, sc_apdu_t *apdu, const u8 *buf,
* | +------------------------------------> | sc_check_apdu |
* | +--------------------+
* | send single APDU +--------------------+
* +---------------------------------------> | do_single_transmit |
* +---------------------------------------> | sc_transmit |
* ^ +--------------------+
* | |
* | re-transmit if wrong length |
@ -267,20 +358,22 @@ static int sc_check_apdu(sc_card_t *card, const sc_apdu_t *apdu)
{
if ((apdu->cse & ~SC_APDU_SHORT_MASK) == 0) {
/* length check for short APDU */
if (apdu->le > 256 || (apdu->lc > 255 &&
(apdu->flags & SC_APDU_FLAGS_CHAINING) == 0))
if (apdu->le > 256 || (apdu->lc > 255 && (apdu->flags & SC_APDU_FLAGS_CHAINING) == 0))
goto error;
} else if ((apdu->cse & SC_APDU_EXT) != 0) {
}
else if ((apdu->cse & SC_APDU_EXT) != 0) {
/* check if the card supports extended APDUs */
if ((card->caps & SC_CARD_CAP_APDU_EXT) == 0) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "card doesn't support extended APDUs");
sc_log(card->ctx, "card doesn't support extended APDUs");
goto error;
}
/* length check for extended APDU */
if (apdu->le > 65536 || apdu->lc > 65535)
goto error;
} else
}
else {
goto error;
}
switch (apdu->cse & SC_APDU_SHORT_MASK) {
case SC_APDU_CASE_1:
@ -296,7 +389,7 @@ static int sc_check_apdu(sc_card_t *card, const sc_apdu_t *apdu)
if (apdu->resplen == 0 || apdu->resp == NULL)
goto error;
/* return buffer to small */
if ((apdu->le == 0 && apdu->resplen < SC_MAX_APDU_BUFFER_SIZE-2)
if ((apdu->le == 0 && apdu->resplen < SC_MAX_APDU_BUFFER_SIZE-2)
|| (apdu->resplen < apdu->le))
goto error;
break;
@ -327,12 +420,12 @@ static int sc_check_apdu(sc_card_t *card, const sc_apdu_t *apdu)
goto error;
break;
default:
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid APDU case %d\n", apdu->cse);
sc_log(card->ctx, "Invalid APDU case %d", apdu->cse);
return SC_ERROR_INVALID_ARGUMENTS;
}
return SC_SUCCESS;
error:
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Invalid Case %d %s APDU:\n"
sc_log(card->ctx, "Invalid Case %d %s APDU:\n"
"cse=%02x cla=%02x ins=%02x p1=%02x p2=%02x lc=%lu le=%lu\n"
"resp=%p resplen=%lu data=%p datalen=%lu",
apdu->cse & SC_APDU_SHORT_MASK,
@ -345,7 +438,7 @@ error:
}
/** Tries to determine the APDU type (short or extended) of the supplied
* APDU if one of the SC_APDU_CASE_? types is used.
* APDU if one of the SC_APDU_CASE_? types is used.
* @param apdu APDU object
*/
static void sc_detect_apdu_cse(const sc_card_t *card, sc_apdu_t *apdu)
@ -365,156 +458,220 @@ static void sc_detect_apdu_cse(const sc_card_t *card, sc_apdu_t *apdu)
}
/** Sends a single APDU to the card reader and calls
* GET RESPONSE to get the return data if necessary.
#ifdef ENABLE_SM
static int
sc_single_sm_transmit(struct sc_card *card, struct sc_apdu *apdu)
{
struct sc_context *ctx = card->ctx;
struct sc_apdu *sm_apdu = NULL;
int rv;
LOG_FUNC_CALLED(ctx);
sc_log(ctx, "SM_MODE:%X", card->sm_ctx.sm_mode);
if (!card->sm_ctx.ops.get_sm_apdu || !card->sm_ctx.ops.free_sm_apdu)
LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED);
/* get SM encoded APDU */
rv = card->sm_ctx.ops.get_sm_apdu(card, apdu, &sm_apdu);
LOG_TEST_RET(ctx, rv, "get SM APDU error");
if (!sm_apdu)
LOG_TEST_RET(ctx, SC_ERROR_INTERNAL, "cannot get SM APDU");
/* check if SM APDU is still valid */
rv = sc_check_apdu(card, sm_apdu);
if (rv < 0) {
card->sm_ctx.ops.free_sm_apdu(card, apdu, &sm_apdu);
LOG_TEST_RET(ctx, rv, "cannot validate SM encoded APDU");
}
/* send APDU to the reader driver */
rv = card->reader->ops->transmit(card->reader, sm_apdu);
LOG_TEST_RET(ctx, rv, "unable to transmit APDU");
/* decode SM answer and free temporary SM related data */
rv = card->sm_ctx.ops.free_sm_apdu(card, apdu, &sm_apdu);
LOG_FUNC_RETURN(ctx, rv);
}
#endif
static int
sc_single_transmit(struct sc_card *card, struct sc_apdu *apdu)
{
struct sc_context *ctx = card->ctx;
int rv;
LOG_FUNC_CALLED(ctx);
if (card->reader->ops->transmit == NULL)
LOG_TEST_RET(card->ctx, SC_ERROR_NOT_SUPPORTED, "cannot transmit APDU");
#ifdef ENABLE_SM
if (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT)
return sc_single_sm_transmit(card, apdu);
#endif
/* send APDU to the reader driver */
rv = card->reader->ops->transmit(card->reader, apdu);
LOG_TEST_RET(ctx, rv, "unable to transmit APDU");
LOG_FUNC_RETURN(ctx, rv);
}
static int
sc_set_le_and_transmit(struct sc_card *card, struct sc_apdu *apdu, size_t olen)
{
struct sc_context *ctx = card->ctx;
size_t nlen = apdu->sw2 ? (size_t)apdu->sw2 : 256;
int rv;
LOG_FUNC_CALLED(ctx);
/* we cannot re-transmit the APDU with the demanded Le value
* as the buffer is too small => error */
if (olen < nlen)
LOG_TEST_RET(ctx, SC_ERROR_WRONG_LENGTH, "wrong length: required length exceeds resplen");
/* don't try again if it doesn't work this time */
apdu->flags |= SC_APDU_FLAGS_NO_GET_RESP;
/* set the new expected length */
apdu->resplen = olen;
apdu->le = nlen;
/* Belpic V1 applets have a problem: if the card sends a 6C XX (only XX bytes available),
* and we resend the command too soon (i.e. the reader is too fast), the card doesn't respond.
* So we build in a delay. */
if (card->type == SC_CARD_TYPE_BELPIC_EID)
msleep(40);
/* re-transmit the APDU with new Le length */
rv = sc_single_transmit(card, apdu);
LOG_TEST_RET(ctx, rv, "cannot re-transmit APDU");
LOG_FUNC_RETURN(ctx, rv);
}
static int
sc_get_response(struct sc_card *card, struct sc_apdu *apdu, size_t olen)
{
struct sc_context *ctx = card->ctx;
size_t le, minlen, buflen;
unsigned char *buf;
int rv;
LOG_FUNC_CALLED(ctx);
if (apdu->le == 0) {
/* no data is requested => change return value to 0x9000 and ignore the remaining data */
apdu->sw1 = 0x90;
apdu->sw2 = 0x00;
return SC_SUCCESS;
}
/* this should _never_ happen */
if (!card->ops->get_response)
LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "no GET RESPONSE command");
/* call GET RESPONSE until we have read all data requested or until the card retuns 0x9000,
* whatever happens first. */
/* if there are already data in response append a new data to the end of the buffer */
buf = apdu->resp + apdu->resplen;
/* read as much data as fits in apdu->resp (i.e. min(apdu->resplen, amount of data available)). */
buflen = olen - apdu->resplen;
/* 0x6100 means at least 256 more bytes to read */
le = apdu->sw2 != 0 ? (size_t)apdu->sw2 : 256;
/* we try to read at least as much as bytes as promised in the response bytes */
minlen = le;
do {
unsigned char resp[256];
size_t resp_len = le;
/* call GET RESPONSE to get more date from the card;
* note: GET RESPONSE returns the left amount of data (== SW2) */
memset(resp, 0, sizeof(resp));
rv = card->ops->get_response(card, &resp_len, resp);
if (rv < 0) {
#ifdef ENABLE_SM
if (resp_len) {
sc_log(ctx, "SM response data %s", sc_dump_hex(resp, resp_len));
sc_sm_update_apdu_response(card, resp, resp_len, rv, apdu);
}
#endif
LOG_TEST_RET(ctx, rv, "GET RESPONSE error");
}
le = resp_len;
/* copy as much as will fit in requested buffer */
if (buflen < le)
le = buflen;
memcpy(buf, resp, le);
buf += le;
buflen -= le;
/* we have all the data the caller requested even if the card has more data */
if (buflen == 0)
break;
minlen -= le;
if (rv != 0)
le = minlen = (size_t)rv;
else
/* if the card has returned 0x9000 but we still expect data ask for more
* until we have read enough bytes */
le = minlen;
} while (rv != 0 || minlen != 0);
/* we've read all data, let's return 0x9000 */
apdu->resplen = buf - apdu->resp;
apdu->sw1 = 0x90;
apdu->sw2 = 0x00;
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}
/** Sends a single APDU to the card reader and calls GET RESPONSE to get the return data if necessary.
* @param card sc_card_t object for the smartcard
* @param apdu APDU to be sent
* @return SC_SUCCESS on success and an error value otherwise
*/
static int do_single_transmit(sc_card_t *card, sc_apdu_t *apdu)
static int
sc_transmit(sc_card_t *card, sc_apdu_t *apdu)
{
int r;
struct sc_context *ctx = card->ctx;
size_t olen = apdu->resplen;
sc_context_t *ctx = card->ctx;
int r;
/* XXX: insert secure messaging here (?), i.e. something like
if (card->sm_ctx->use_sm != 0) {
r = card->ops->sm_transform(...);
if (r != SC_SUCCESS)
...
r = sc_check_apdu(...);
if (r != SC_SUCCESS)
...
}
*/
LOG_FUNC_CALLED(ctx);
/* send APDU to the reader driver */
if (card->reader->ops->transmit == NULL)
return SC_ERROR_NOT_SUPPORTED;
r = card->reader->ops->transmit(card->reader, apdu);
if (r != 0) {
sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "unable to transmit APDU");
return r;
}
/* ok, the APDU was successfully transmitted. Now we have two
* special cases:
* 1. the card returned 0x6Cxx: in this case we re-trasmit the APDU
* wit hLe set to SW2 (this is course only possible if the
* response buffer size is larger than the new Le = SW2)
r = sc_single_transmit(card, apdu);
LOG_TEST_RET(ctx, r, "transmit APDU failed");
/* ok, the APDU was successfully transmitted. Now we have two special cases:
* 1. the card returned 0x6Cxx: in this case APDU will be re-trasmitted with Le set to SW2
* (possible only if response buffer size is larger than new Le = SW2)
*/
if (apdu->sw1 == 0x6C && (apdu->flags & SC_APDU_FLAGS_NO_RETRY_WL) == 0) {
size_t nlen = apdu->sw2 != 0 ? (size_t)apdu->sw2 : 256;
if (olen >= nlen) {
/* don't try again if it doesn't work this time */
apdu->flags |= SC_APDU_FLAGS_NO_GET_RESP;
/* set the new expected length */
apdu->resplen = olen;
apdu->le = nlen;
/* Belpic V1 applets have a problem: if the card sends a 6C XX
* (only XX bytes available), and we resend the command too soon
* (i.e. the reader is too fast), the card doesn't respond. So
* we build in a delay. */
if (card->type == SC_CARD_TYPE_BELPIC_EID)
msleep(40);
/* re-transmit the APDU with new Le length */
r = card->reader->ops->transmit(card->reader, apdu);
if (r != SC_SUCCESS) {
sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "unable to transmit APDU");
return r;
}
} else {
/* we cannot re-transmit the APDU with the demanded
* Le value as the buffer is too small => error */
sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "wrong length: required length exceeds resplen");
return SC_ERROR_WRONG_LENGTH;
}
}
if (apdu->sw1 == 0x6C && (apdu->flags & SC_APDU_FLAGS_NO_RETRY_WL) == 0)
r = sc_set_le_and_transmit(card, apdu, olen);
LOG_TEST_RET(ctx, r, "cannot re-transmit APDU ");
/* 2. the card returned 0x61xx: more data can be read from the card
* using the GET RESPONSE command (mostly used in the T0 protocol).
* Unless the SC_APDU_FLAGS_NO_GET_RESP is set we try to read as
* much data as possible using GET RESPONSE.
*/
if (apdu->sw1 == 0x61 && (apdu->flags & SC_APDU_FLAGS_NO_GET_RESP) == 0) {
if (apdu->le == 0) {
/* no data is requested => change return value to
* 0x9000 and ignore the remaining data */
/* FIXME: why not return 0x61xx ? It's not an
* error */
apdu->sw1 = 0x90;
apdu->sw2 = 0x00;
} else {
/* call GET RESPONSE until we have read all data
* requested or until the card retuns 0x9000,
* whatever happens first.
*/
size_t le, minlen, buflen;
u8 *buf;
if (apdu->sw1 == 0x61 && (apdu->flags & SC_APDU_FLAGS_NO_GET_RESP) == 0)
r = sc_get_response(card, apdu, olen);
LOG_TEST_RET(ctx, r, "cannot get all data with 'GET RESPONSE'");
if (card->ops->get_response == NULL) {
/* this should _never_ happen */
sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "no GET RESPONSE command\n");
return SC_ERROR_NOT_SUPPORTED;
}
/* if the command already returned some data
* append the new data to the end of the buffer
*/
buf = apdu->resp + apdu->resplen;
/* read as much data as fits in apdu->resp (i.e.
* max(apdu->resplen, amount of data available)).
*/
buflen = olen - apdu->resplen;
/* 0x6100 means at least 256 more bytes to read */
le = apdu->sw2 != 0 ? (size_t)apdu->sw2 : 256;
/* we try to read at least as much as bytes as
* promised in the response bytes */
minlen = le;
do {
u8 tbuf[256];
/* call GET RESPONSE to get more date from
* the card; note: GET RESPONSE returns the
* amount of data left (== SW2) */
r = card->ops->get_response(card, &le, tbuf);
if (r < 0)
SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r);
if (buflen < le)
/* copy as much as will fit in requested buffer */
le = buflen;
memcpy(buf, tbuf, le);
buf += le;
buflen -= le;
/* we have all the data the caller requested
* even if the card has more data */
if (buflen == 0)
break;
minlen -= le;
if (r != 0)
le = minlen = (size_t)r;
else
/* if the card has returned 0x9000 but
* we still expect data ask for more
* until we have read enough bytes */
le = minlen;
} while (r != 0 || minlen != 0);
/* we've read all data, let's return 0x9000 */
apdu->resplen = buf - apdu->resp;
apdu->sw1 = 0x90;
apdu->sw2 = 0x00;
}
}
return SC_SUCCESS;
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}
int sc_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu)
{
int r = SC_SUCCESS;
@ -522,7 +679,7 @@ int sc_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu)
if (card == NULL || apdu == NULL)
return SC_ERROR_INVALID_ARGUMENTS;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
LOG_FUNC_CALLED(card->ctx);
/* determine the APDU type if necessary, i.e. to use
* short or extended APDUs */
@ -534,9 +691,9 @@ int sc_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu)
r = sc_lock(card); /* acquire card lock*/
if (r != SC_SUCCESS) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "unable to acquire lock");
sc_log(card->ctx, "unable to acquire lock");
return r;
}
}
if ((apdu->flags & SC_APDU_FLAGS_CHAINING) != 0) {
/* divide et impera: transmit APDU in chunks with Lc <= max_send_size
@ -576,15 +733,15 @@ int sc_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu)
r = sc_check_apdu(card, &tapdu);
if (r != SC_SUCCESS) {
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "inconsistent APDU while chaining");
sc_log(card->ctx, "inconsistent APDU while chaining");
break;
}
r = do_single_transmit(card, &tapdu);
r = sc_transmit(card, &tapdu);
if (r != SC_SUCCESS)
break;
if (last != 0) {
/* in case of the last APDU set the SW1
/* in case of the last APDU set the SW1
* and SW2 bytes in the original APDU */
apdu->sw1 = tapdu.sw1;
apdu->sw2 = tapdu.sw2;
@ -598,123 +755,130 @@ int sc_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu)
len -= plen;
buf += plen;
}
} else
} else
/* transmit single APDU */
r = do_single_transmit(card, apdu);
r = sc_transmit(card, apdu);
/* all done => release lock */
if (sc_unlock(card) != SC_SUCCESS)
sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "sc_unlock failed");
sc_log(card->ctx, "sc_unlock failed");
return r;
}
int sc_bytes2apdu(sc_context_t *ctx, const u8 *buf, size_t len, sc_apdu_t *apdu)
int
sc_bytes2apdu(sc_context_t *ctx, const u8 *buf, size_t len, sc_apdu_t *apdu)
{
const u8 *p;
size_t len0;
const unsigned char *p;
size_t len0;
if (!buf || !apdu)
return SC_ERROR_INVALID_ARGUMENTS;
if (!buf || !apdu)
return SC_ERROR_INVALID_ARGUMENTS;
len0 = len;
if (len < 4) {
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "APDU too short (must be at least 4 bytes)");
return SC_ERROR_INVALID_DATA;
}
len0 = len;
if (len < 4) {
sc_log(ctx, "APDU too short (must be at least 4 bytes)");
return SC_ERROR_INVALID_DATA;
}
memset(apdu, 0, sizeof *apdu);
p = buf;
apdu->cla = *p++;
apdu->ins = *p++;
apdu->p1 = *p++;
apdu->p2 = *p++;
len -= 4;
if (!len) {
apdu->cse = SC_APDU_CASE_1;
} else {
if (*p == 0 && len >= 3) {
/* ...must be an extended APDU */
p++;
if (len == 3) {
apdu->le = (*p++)<<8;
apdu->le += *p++;
if (apdu->le == 0)
apdu->le = 0xffff+1;
len -= 3;
apdu->cse = SC_APDU_CASE_2_EXT;
} else {
/* len > 3 */
apdu->lc = (*p++)<<8;
apdu->lc += *p++;
len -= 3;
if (len < apdu->lc) {
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "APDU too short (need %lu more bytes)\n",
(unsigned long) apdu->lc - len);
return SC_ERROR_INVALID_DATA;
}
apdu->data = p;
apdu->datalen = apdu->lc;
len -= apdu->lc;
p += apdu->lc;
if (!len) {
apdu->cse = SC_APDU_CASE_3_EXT;
} else {
/* at this point the apdu has a Lc, so Le is on 2 bytes */
if (len < 2) {
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "APDU too short (need 2 more bytes)\n");
return SC_ERROR_INVALID_DATA;
}
apdu->le = (*p++)<<8;
apdu->le += *p++;
if (apdu->le == 0)
apdu->le = 0xffff+1;
len -= 2;
apdu->cse = SC_APDU_CASE_4_EXT;
}
}
} else {
/* ...must be a short APDU */
if (len == 1) {
apdu->le = *p++;
if (apdu->le == 0)
apdu->le = 0xff+1;
len--;
apdu->cse = SC_APDU_CASE_2_SHORT;
} else {
apdu->lc = *p++;
len--;
if (len < apdu->lc) {
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "APDU too short (need %lu more bytes)\n",
(unsigned long) apdu->lc - len);
return SC_ERROR_INVALID_DATA;
}
apdu->data = p;
apdu->datalen = apdu->lc;
len -= apdu->lc;
p += apdu->lc;
if (!len) {
apdu->cse = SC_APDU_CASE_3_SHORT;
} else {
apdu->le = *p++;
if (apdu->le == 0)
apdu->le = 0xff+1;
len--;
apdu->cse = SC_APDU_CASE_4_SHORT;
memset(apdu, 0, sizeof *apdu);
p = buf;
apdu->cla = *p++;
apdu->ins = *p++;
apdu->p1 = *p++;
apdu->p2 = *p++;
len -= 4;
}
}
}
if (len) {
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "APDU too long (%lu bytes extra)\n",
(unsigned long) len);
return SC_ERROR_INVALID_DATA;
}
}
if (!len) {
apdu->cse = SC_APDU_CASE_1;
sc_log(ctx, "CASE_1 APDU: %lu bytes:\tins=%02x p1=%02x p2=%02x lc=%04x le=%04x",
(unsigned long) len0, apdu->ins, apdu->p1, apdu->p2, apdu->lc, apdu->le);
return SC_SUCCESS;
}
sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Case %d %s APDU, %lu bytes:\tins=%02x p1=%02x p2=%02x lc=%04x le=%04x",
apdu->cse & SC_APDU_SHORT_MASK,
(apdu->cse & SC_APDU_EXT) != 0 ? "extended" : "short",
(unsigned long) len0, apdu->ins, apdu->p1, apdu->p2, apdu->lc, apdu->le);
if (*p == 0 && len >= 3) {
/* ...must be an extended APDU */
p++;
if (len == 3) {
apdu->le = (*p++)<<8;
apdu->le += *p++;
if (apdu->le == 0)
apdu->le = 0xffff+1;
len -= 3;
apdu->cse = SC_APDU_CASE_2_EXT;
}
else {
/* len > 3 */
apdu->lc = (*p++)<<8;
apdu->lc += *p++;
len -= 3;
if (len < apdu->lc) {
sc_log(ctx, "APDU too short (need %lu more bytes)", (unsigned long) apdu->lc - len);
return SC_ERROR_INVALID_DATA;
}
apdu->data = p;
apdu->datalen = apdu->lc;
len -= apdu->lc;
p += apdu->lc;
if (!len) {
apdu->cse = SC_APDU_CASE_3_EXT;
}
else {
/* at this point the apdu has a Lc, so Le is on 2 bytes */
if (len < 2) {
sc_debug(ctx, SC_LOG_DEBUG_VERBOSE, "APDU too short (need 2 more bytes)\n");
return SC_ERROR_INVALID_DATA;
}
apdu->le = (*p++)<<8;
apdu->le += *p++;
if (apdu->le == 0)
apdu->le = 0xffff+1;
len -= 2;
apdu->cse = SC_APDU_CASE_4_EXT;
}
}
}
else {
/* ...must be a short APDU */
if (len == 1) {
apdu->le = *p++;
if (apdu->le == 0)
apdu->le = 0xff+1;
len--;
apdu->cse = SC_APDU_CASE_2_SHORT;
}
else {
apdu->lc = *p++;
len--;
if (len < apdu->lc) {
sc_log(ctx, "APDU too short (need %lu more bytes)", (unsigned long) apdu->lc - len);
return SC_ERROR_INVALID_DATA;
}
apdu->data = p;
apdu->datalen = apdu->lc;
len -= apdu->lc;
p += apdu->lc;
if (!len) {
apdu->cse = SC_APDU_CASE_3_SHORT;
}
else {
apdu->le = *p++;
if (apdu->le == 0)
apdu->le = 0xff+1;
len--;
apdu->cse = SC_APDU_CASE_4_SHORT;
}
}
}
if (len) {
sc_log(ctx, "APDU too long (%lu bytes extra)",(unsigned long) len);
return SC_ERROR_INVALID_DATA;
}
return SC_SUCCESS;
sc_log(ctx, "Case %d %s APDU, %lu bytes:\tins=%02x p1=%02x p2=%02x lc=%04x le=%04x",
apdu->cse & SC_APDU_SHORT_MASK,
(apdu->cse & SC_APDU_EXT) != 0 ? "extended" : "short",
(unsigned long) len0, apdu->ins, apdu->p1, apdu->p2, apdu->lc, apdu->le);
return SC_SUCCESS;
}

View File

@ -34,6 +34,12 @@
#define INVALIDATE_CARD_CACHE_IN_UNLOCK
*/
#ifdef ENABLE_SM
static int sc_card_sm_load(sc_card_t *card, const char *path, const char *module);
static int sc_card_sm_unload(sc_card_t *card);
static int sc_card_sm_check(sc_card_t *card);
#endif
int sc_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2)
{
if (card == NULL)
@ -93,6 +99,10 @@ static void sc_card_free(sc_card_t *card)
free(card->ops);
if (card->algorithms != NULL)
free(card->algorithms);
if (card->cache.current_ef)
sc_file_free(card->cache.current_ef);
if (card->cache.current_df)
sc_file_free(card->cache.current_df);
if (card->mutex != NULL) {
int r = sc_mutex_destroy(card->ctx, card->mutex);
if (r != SC_SUCCESS)
@ -133,7 +143,7 @@ int sc_connect_card(sc_reader_t *reader, sc_card_t **card_out)
/* See if the ATR matches any ATR specified in the config file */
if ((driver = ctx->forced_driver) == NULL) {
sc_debug(ctx, SC_LOG_DEBUG_MATCH, "matching configured ATRs");
sc_log(ctx, "matching configured ATRs");
for (i = 0; ctx->card_drivers[i] != NULL; i++) {
driver = ctx->card_drivers[i];
@ -142,12 +152,12 @@ int sc_connect_card(sc_reader_t *reader, sc_card_t **card_out)
driver = NULL;
continue;
}
sc_debug(ctx, SC_LOG_DEBUG_MATCH, "trying driver: %s", driver->short_name);
sc_log(ctx, "trying driver '%s'", driver->short_name);
idx = _sc_match_atr(card, driver->atr_map, NULL);
if (idx >= 0) {
struct sc_atr_table *src = &driver->atr_map[idx];
sc_debug(ctx, SC_LOG_DEBUG_MATCH, "matched: %s", driver->name);
sc_log(ctx, "matched driver '%s'", driver->name);
/* It's up to card driver to notice these correctly */
card->name = src->name;
card->type = src->type;
@ -166,8 +176,7 @@ int sc_connect_card(sc_reader_t *reader, sc_card_t **card_out)
if (card->ops->init != NULL) {
r = card->ops->init(card);
if (r) {
sc_debug(ctx, SC_LOG_DEBUG_MATCH, "driver '%s' init() failed: %s",
card->driver->name, sc_strerror(r));
sc_log(ctx, "driver '%s' init() failed: %s", card->driver->name, sc_strerror(r));
goto err;
}
}
@ -177,20 +186,19 @@ int sc_connect_card(sc_reader_t *reader, sc_card_t **card_out)
struct sc_card_driver *drv = ctx->card_drivers[i];
const struct sc_card_operations *ops = drv->ops;
sc_debug(ctx, SC_LOG_DEBUG_MATCH, "trying driver: %s", drv->short_name);
sc_log(ctx, "trying driver '%s'", drv->short_name);
if (ops == NULL || ops->match_card == NULL)
continue;
/* Needed if match_card() needs to talk with the card (e.g. card-muscle) */
*card->ops = *ops;
if (ops->match_card(card) != 1)
continue;
sc_debug(ctx, SC_LOG_DEBUG_MATCH, "matched: %s", drv->name);
sc_log(ctx, "matched: %s", drv->name);
memcpy(card->ops, ops, sizeof(struct sc_card_operations));
card->driver = drv;
r = ops->init(card);
if (r) {
sc_debug(ctx, SC_LOG_DEBUG_MATCH, "driver '%s' init() failed: %s", drv->name,
sc_strerror(r));
sc_log(ctx, "driver '%s' init() failed: %s", drv->name, sc_strerror(r));
if (r == SC_ERROR_INVALID_CARD) {
card->driver = NULL;
continue;
@ -201,7 +209,7 @@ int sc_connect_card(sc_reader_t *reader, sc_card_t **card_out)
}
}
if (card->driver == NULL) {
sc_debug(ctx, SC_LOG_DEBUG_MATCH, "unable to find driver for inserted card");
sc_log(ctx, "unable to find driver for inserted card");
r = SC_ERROR_INVALID_CARD;
goto err;
}
@ -222,6 +230,16 @@ int sc_connect_card(sc_reader_t *reader, sc_card_t **card_out)
sc_log(ctx, "card info name:'%s', type:%i, flags:0x%X, max_send/recv_size:%i/%i",
card->name, card->type, card->flags, card->max_send_size, card->max_recv_size);
#ifdef ENABLE_SM
/* Check, if secure messaging module present. */
r = sc_card_sm_check(card);
if (r) {
sc_log(ctx, "cannot load secure messaging module");
goto err;
}
#endif
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
err:
if (connected)
@ -254,6 +272,11 @@ int sc_disconnect_card(sc_card_t *card)
sc_log(ctx, "disconnect() failed: %s", sc_strerror(r));
}
#ifdef ENABLE_SM
/* release SM related resources */
sc_card_sm_unload(card);
#endif
sc_card_free(card);
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}
@ -290,7 +313,7 @@ int sc_lock(sc_card_t *card)
int r = 0, r2 = 0;
LOG_FUNC_CALLED(card->ctx);
if (card == NULL)
return SC_ERROR_INVALID_ARGUMENTS;
r = sc_mutex_lock(card->ctx, card->mutex);
@ -423,6 +446,13 @@ int sc_read_binary(sc_card_t *card, unsigned int idx,
if (count == 0)
return 0;
#ifdef ENABLE_SM
if (card->sm_ctx.ops.read_binary) {
r = card->sm_ctx.ops.read_binary(card, idx, buf, count);
if (r)
LOG_FUNC_RETURN(card->ctx, r);
}
#endif
if (card->ops->read_binary == NULL)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
@ -508,6 +538,15 @@ int sc_update_binary(sc_card_t *card, unsigned int idx,
sc_log(card->ctx, "called; %d bytes at index %d", count, idx);
if (count == 0)
return 0;
#ifdef ENABLE_SM
if (card->sm_ctx.ops.update_binary) {
r = card->sm_ctx.ops.update_binary(card, idx, buf, count);
if (r)
LOG_FUNC_RETURN(card->ctx, r);
}
#endif
if (card->ops->update_binary == NULL)
LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
@ -786,7 +825,7 @@ sc_algorithm_info_t * sc_card_find_ec_alg(sc_card_t *card,
{
return sc_card_find_alg(card, SC_ALGORITHM_EC, key_length);
}
int _sc_card_add_rsa_alg(sc_card_t *card, unsigned int key_length,
unsigned long flags, unsigned long exponent)
{
@ -854,7 +893,7 @@ static int match_atr_table(sc_context_t *ctx, struct sc_atr_table *table, struct
mbin_len = sizeof(mbin);
sc_hex_to_bin(matr, mbin, &mbin_len);
if (mbin_len != fix_bin_len) {
sc_log(ctx, "length of atr and atr mask do not match - ignored: %s - %s", tatr, matr);
sc_log(ctx, "length of atr and atr mask do not match - ignored: %s - %s", tatr, matr);
continue;
}
for (s = 0; s < tbin_len; s++) {
@ -982,10 +1021,10 @@ scconf_block *sc_get_conf_block(sc_context_t *ctx, const char *name1, const char
{
int i;
scconf_block *conf_block = NULL;
for (i = 0; ctx->conf_blocks[i] != NULL; i++) {
scconf_block **blocks;
blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], name1, name2);
if (blocks != NULL) {
conf_block = blocks[0];
@ -1005,7 +1044,7 @@ void sc_print_cache(struct sc_card *card) {
if (!card->cache.valid || (!card->cache.current_ef && !card->cache.current_df)) {
sc_log(ctx, "card cache invalid");
return;
return;
}
if (card->cache.current_ef)
@ -1018,3 +1057,206 @@ void sc_print_cache(struct sc_card *card) {
sc_print_path(&card->cache.current_df->path));
}
#ifdef ENABLE_SM
static int
sc_card_sm_unload(struct sc_card *card)
{
if (card->sm_ctx.module.ops.module_cleanup)
card->sm_ctx.module.ops.module_cleanup(card->ctx);
if (card->sm_ctx.module.handle)
sc_dlclose(card->sm_ctx.module.handle);
card->sm_ctx.module.handle = NULL;
return 0;
}
static int
sc_card_sm_load(struct sc_card *card, const char *module_path, const char *in_module)
{
struct sc_context *ctx = NULL;
int rv = SC_ERROR_INTERNAL;
char *module = NULL;
#ifdef _WIN32
char temp_path[PATH_MAX];
int temp_len;
long rc;
HKEY hKey;
const char path_delim = '\\';
#else
const char path_delim = '/';
#endif
assert(card != NULL);
ctx = card->ctx;
SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL);
if (!in_module)
return sc_card_sm_unload(card);
#ifdef _WIN32
if (!module_path) {
rc = RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\OpenSC Project\\OpenSC", 0, KEY_QUERY_VALUE, &hKey );
if( rc == ERROR_SUCCESS ) {
temp_len = PATH_MAX;
rc = RegQueryValueEx( hKey, "SmDir", NULL, NULL, (LPBYTE) temp_path, &temp_len);
if( (rc == ERROR_SUCCESS) && (temp_len < PATH_MAX) )
module_path = temp_path;
RegCloseKey( hKey );
}
}
if (!module_path) {
rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\OpenSC Project\\OpenSC", 0, KEY_QUERY_VALUE, &hKey );
if( rc == ERROR_SUCCESS ) {
temp_len = PATH_MAX;
rc = RegQueryValueEx( hKey, "SmDir", NULL, NULL, (LPBYTE) temp_path, &temp_len);
if(rc == ERROR_SUCCESS && temp_len < PATH_MAX)
module_path = temp_path;
RegCloseKey( hKey );
}
}
#endif
sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "SM module '%s' located in '%s'", in_module, module_path);
if (module_path) {
int sz = strlen(in_module) + strlen(module_path) + 3;
module = malloc(sz);
if (module)
snprintf(module, sz, "%s%c%s", module_path, path_delim, in_module);
}
else {
module = strdup(in_module);
}
if (!module)
return SC_ERROR_MEMORY_FAILURE;
sc_log(ctx, "try to load SM module '%s'", module);
do {
struct sm_module_operations *mod_ops = &card->sm_ctx.module.ops;
void *mod_handle;
card->sm_ctx.module.handle = sc_dlopen(module);
if (!card->sm_ctx.module.handle) {
sc_log(ctx, "cannot open dynamic library '%s': %s", module, sc_dlerror());
break;
}
mod_handle = card->sm_ctx.module.handle;
mod_ops->initialize = sc_dlsym(mod_handle, "initialize");
if (!mod_ops->initialize) {
sc_log(ctx, "SM handler 'initialize' not exported: %s", sc_dlerror());
break;
}
mod_ops->get_apdus = sc_dlsym(mod_handle, "get_apdus");
if (!mod_ops->get_apdus) {
sc_log(ctx, "SM handler 'get_apdus' not exported: %s", sc_dlerror());
break;
}
mod_ops->finalize = sc_dlsym(mod_handle, "finalize");
if (!mod_ops->finalize)
sc_log(ctx, "SM handler 'finalize' not exported -- ignored");
mod_ops->module_init = sc_dlsym(mod_handle, "module_init");
if (!mod_ops->module_init)
sc_log(ctx, "SM handler 'module_init' not exported -- ignored");
mod_ops->module_cleanup = sc_dlsym(mod_handle, "module_cleanup");
if (!mod_ops->module_cleanup)
sc_log(ctx, "SM handler 'module_cleanup' not exported -- ignored");
mod_ops->test = sc_dlsym(mod_handle, "test");
if (mod_ops->test)
sc_log(ctx, "SM handler 'test' not exported -- ignored");
rv = 0;
break;
} while(0);
if (rv)
sc_card_sm_unload(card);
card->sm_ctx.sm_mode = SM_MODE_ACL;
if (module)
free(module);
SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, rv);
}
/* get SM related configuration settings and initialize SM session, SM module, ... */
static int
sc_card_sm_check(struct sc_card *card)
{
const char *sm = NULL, *module_name = NULL, *module_path = NULL, *module_data = NULL, *sm_mode = NULL;
struct sc_context *ctx = card->ctx;
scconf_block *atrblock = NULL, *sm_conf_block = NULL;
int rv, ii;
SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_NORMAL);
/* get the name of card specific SM configuration section */
atrblock = _sc_match_atr_block(ctx, card->driver, &card->atr);
if (atrblock == NULL)
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
sm = scconf_get_str(atrblock, "secure_messaging", NULL);
if (!sm)
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
/* get SM configuration section by the name */
sc_log(ctx, "secure_messaging configuration block '%s'", sm);
for (ii = 0; ctx->conf_blocks[ii]; ii++) {
scconf_block **blocks;
blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[ii], "secure_messaging", sm);
if (blocks) {
sm_conf_block = blocks[0];
free(blocks);
}
if (sm_conf_block != NULL)
break;
}
if (!sm_conf_block)
LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_CONFIGURATION, "SM configuration block not preset");
/* check if an external SM module has to be used */
module_path = scconf_get_str(sm_conf_block, "module_path", NULL);
module_name = scconf_get_str(sm_conf_block, "module_name", NULL);
sc_log(ctx, "SM module '%s' in '%s'", module_name, module_path);
if (!module_name)
LOG_TEST_RET(ctx, SC_ERROR_INCONSISTENT_CONFIGURATION, "Invalid SM configuration: module not defined");
rv = sc_card_sm_load(card, module_path, module_name);
LOG_TEST_RET(ctx, rv, "Failed to load SM module");
strncpy(card->sm_ctx.module.filename, module_name, sizeof(card->sm_ctx.module.filename));
strncpy(card->sm_ctx.config_section, sm, sizeof(card->sm_ctx.config_section));
/* allocate resources for the external SM module */
sc_log(ctx, "'module_init' handler %p", card->sm_ctx.module.ops.module_init);
if (card->sm_ctx.module.ops.module_init) {
module_data = scconf_get_str(sm_conf_block, "module_data", NULL);
sc_log(ctx, "module_data '%s'", module_data);
rv = card->sm_ctx.module.ops.module_init(ctx, module_data);
SC_TEST_RET(ctx, SC_LOG_DEBUG_NORMAL, rv, "Cannot initialize SM module");
}
/* initialize SM session in the case of 'APDU TRANSMIT' SM mode */
sm_mode = scconf_get_str(sm_conf_block, "mode", NULL);
sc_log(ctx, "SM mode '%s'; 'open' handler %p", sm_mode, card->sm_ctx.ops.open);
if (sm_mode && !strcasecmp("Transmit", sm_mode)) {
if (!card->sm_ctx.ops.open || !card->sm_ctx.ops.get_sm_apdu || !card->sm_ctx.ops.free_sm_apdu)
LOG_TEST_RET(ctx, SC_ERROR_NOT_SUPPORTED, "'Transmit' SM asked but not supported by card driver");
card->sm_ctx.sm_mode = SM_MODE_TRANSMIT;
rv = card->sm_ctx.ops.open(card);
LOG_TEST_RET(ctx, rv, "Cannot initialize SM");
}
sc_log(ctx, "SM mode:%X", card->sm_ctx.sm_mode);
SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, rv);
}
#endif

View File

@ -40,6 +40,10 @@ extern "C" {
#include "scconf/scconf.h"
#include "libopensc/errors.h"
#include "libopensc/types.h"
#ifdef ENABLE_SM
#include "libopensc/sm.h"
#endif
#define SC_SEC_OPERATION_DECIPHER 0x0001
#define SC_SEC_OPERATION_SIGN 0x0002
@ -523,6 +527,9 @@ typedef struct sc_card {
sc_serial_number_t serialnr;
void *mutex;
#ifdef ENABLE_SM
struct sm_context sm_ctx;
#endif
unsigned int magic;
} sc_card_t;

344
src/libopensc/sm.h Normal file
View File

@ -0,0 +1,344 @@
/*
* sm.h: Support of Secure Messaging
*
* Copyright (C) 2010 Viktor Tarasov <vtarasov@opentrust.com>
* OpenTrust <www.opentrust.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 _SM_H
#define _SM_H
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include <libopensc/errors.h>
#include <libopensc/types.h>
#include <common/libscdl.h>
#ifndef SHA_DIGEST_LENGTH
#define SHA_DIGEST_LENGTH 20
#define SHA1_DIGEST_LENGTH 20
#define SHA256_DIGEST_LENGTH 32
#endif
#define SM_TYPE_GP_SCP01 0x100
#define SM_TYPE_CWA14890 0x400
#define SM_MODE_NONE 0x0
#define SM_MODE_ACL 0x100
#define SM_MODE_TRANSMIT 0x200
#define SM_CMD_INITIALIZE 0x10
#define SM_CMD_MUTUAL_AUTHENTICATION 0x20
#define SM_CMD_RSA 0x100
#define SM_CMD_RSA_GENERATE 0x101
#define SM_CMD_RSA_UPDATE 0x102
#define SM_CMD_RSA_READ_PUBLIC 0x103
#define SM_CMD_FILE 0x200
#define SM_CMD_FILE_READ 0x201
#define SM_CMD_FILE_UPDATE 0x202
#define SM_CMD_FILE_CREATE 0x203
#define SM_CMD_FILE_DELETE 0x204
#define SM_CMD_PIN 0x300
#define SM_CMD_PIN_VERIFY 0x301
#define SM_CMD_PIN_RESET 0x302
#define SM_CMD_PIN_SET_PIN 0x303
#define SM_CMD_PSO 0x400
#define SM_CMD_PSO_DST 0x401
#define SM_CMD_APDU 0x500
#define SM_CMD_APDU_TRANSMIT 0x501
#define SM_CMD_APDU_RAW 0x502
#define SM_CMD_APPLET 0x600
#define SM_CMD_APPLET_DELETE 0x601
#define SM_CMD_APPLET_LOAD 0x602
#define SM_CMD_APPLET_INSTALL 0x603
#define SM_CMD_EXTERNAL_AUTH 0x700
#define SM_CMD_EXTERNAL_AUTH_INIT 0x701
#define SM_CMD_EXTERNAL_AUTH_CHALLENGE 0x702
#define SM_CMD_EXTERNAL_AUTH_DOIT 0x703
#define SM_CMD_SDO_UPDATE 0x800
#define SM_CMD_FINALIZE 0x900
#define SM_RESPONSE_CONTEXT_TAG 0xA1
#define SM_RESPONSE_CONTEXT_DATA_TAG 0xA2
#define SM_MAX_DATA_SIZE 0xE0
#define SM_SMALL_CHALLENGE_LEN 8
#define SM_GP_SECURITY_NO 0x00
#define SM_GP_SECURITY_MAC 0x01
#define SM_GP_SECURITY_ENC 0x03
/* Global Platform (SCP01) data types */
/*
* @struct sm_type_params_gp
* Global Platform SM channel parameters
*/
struct sm_type_params_gp {
unsigned level;
unsigned index;
unsigned version;
struct sc_cplc cplc;
};
/*
* @struct sm_gp_keyset
* Global Platform keyset:
* - version, index;
* - keyset presented in three parts: 'ENC', 'MAC' and 'KEK';
* - keyset presented in continuous manner - raw or 'to be diversified'.
*/
struct sm_gp_keyset {
int version;
int index;
unsigned char enc[16];
unsigned char mac[16];
unsigned char kek[16];
unsigned char kmc[48];
unsigned kmc_len;
};
/*
* @struct sm_gp_session
* Global Platform SM session data
*/
struct sm_gp_session {
unsigned char *session_enc, *session_mac, *session_kek;
unsigned char mac_icv[8];
};
/* CWA, IAS/ECC data types */
/*
* @struct sm_type_params_cwa
*/
struct sm_type_params_cwa {
struct sc_crt crt_at;
};
/*
* @struct sm_cwa_keyset
* CWA keyset:
* - SDO reference;
* - 'ENC' and 'MAC' 3DES keys.
*/
struct sm_cwa_keyset {
unsigned sdo_reference;
unsigned char enc[16];
unsigned char mac[16];
};
/*
* @struct sm_cwa_token_data
* CWA token data:
* - serial;
* - 'small' random;
* - 'big' random.
*/
struct sm_cwa_token_data {
unsigned char sn[8];
unsigned char rnd[8];
unsigned char k[32];
};
/*
* @struct sm_cwa_session
* CWA working SM session data:
* - ICC and IFD token data;
* - ENC and MAC session keys;
* - SSC (SM Sequence Counter);
* - 'mutual authentication' data.
*/
struct sm_cwa_session {
struct sm_cwa_token_data icc;
struct sm_cwa_token_data ifd;
unsigned char session_enc[16];
unsigned char session_mac[16];
unsigned char ssc[8];
unsigned char mdata[0x48];
size_t mdata_len;
};
/*
* @struct sc_secure channel
* data type to open and maintain the Secure Messaging session.
*/
struct sm_secure_channel {
union {
struct sm_gp_keyset gp;
struct sm_cwa_keyset cwa;
} keyset;
union {
struct sm_gp_session gp;
struct sm_cwa_session cwa;
} session;
unsigned char host_challenge[SM_SMALL_CHALLENGE_LEN];
unsigned char card_challenge[SM_SMALL_CHALLENGE_LEN];
};
/*
* @struct sc_info is the
* placehold for the secure messaging working data:
* - SM type;
* - SM session state;
* - command to execute by external SM module;
* - data related to the current card context.
*/
struct sm_info {
char config_section[64];
unsigned card_type;
unsigned cmd;
void *cmd_data;
unsigned sm_type;
union {
struct sm_type_params_gp gp;
struct sm_type_params_cwa cwa;
} sm_params;
struct sc_serial_number serialnr;
unsigned security_condition;
struct sc_path current_path_df;
struct sc_path current_path_ef;
struct sc_aid current_aid;
unsigned char *rdata;
size_t rdata_len;
struct sm_secure_channel schannel;
};
/*
* @struct sm_card_response
* data type to return card response.
*/
typedef struct sm_card_response {
int num;
unsigned char data[SC_MAX_APDU_BUFFER_SIZE];
size_t data_len;
unsigned char mac[8];
size_t mac_len;
unsigned char sw1, sw2;
struct sm_card_response *next;
struct sm_card_response *prev;
} sm_card_response_t;
struct sc_context;
struct sc_card;
/*
* @struct sm_card_operations
* card driver handlers related to secure messaging (in 'APDU TRANSMIT' mode)
* - 'open' - initialize SM session;
* - 'encode apdu' - SM encoding of the raw APDU;
* - 'decrypt response' - decode card answer;
* - 'close' - close SM session.
*/
struct sm_card_operations {
int (*open)(struct sc_card *card);
int (*get_sm_apdu)(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu);
int (*free_sm_apdu)(struct sc_card *card, struct sc_apdu *apdu, struct sc_apdu **sm_apdu);
int (*close)(struct sc_card *card);
int (*read_binary)(struct sc_card *card, unsigned int idx,
unsigned char * buf, size_t count);
int (*update_binary)(struct sc_card *card, unsigned int idx,
const unsigned char * buf, size_t count);
};
/*
* @struct sm_module_operations
* API to use external SM modules:
* - 'initiliaze' - get APDU(s) to initialize SM session;
* - 'get apdus' - get secured APDUs to execute particular command;
* - 'finalize' - get APDU(s) to finalize SM session;
* - 'module init' - initialize external module (allocate data, read configuration, ...);
* - 'module cleanup' - free resources allocated by external module.
*/
struct sm_module_operations {
int (*initialize)(struct sc_context *ctx, struct sm_info *info,
struct sc_remote_data *out);
int (*get_apdus)(struct sc_context *ctx, struct sm_info *sm_info,
unsigned char *init_data, size_t init_len,
struct sc_remote_data *out);
int (*finalize)(struct sc_context *ctx, struct sm_info *info, struct sc_remote_data *rdata,
unsigned char *out, size_t out_len);
int (*module_init)(struct sc_context *ctx, const char *data);
int (*module_cleanup)(struct sc_context *ctx);
int (*test)(struct sc_context *ctx, struct sm_info *info, char *out);
};
typedef struct sm_module {
char filename[128];
void *handle;
struct sm_module_operations ops;
} sm_module_t;
/* @struct sm_context
* SM context -- top level of the SM data type
* - SM mode ('ACL' or 'APDU TRANSMIT'), flags;
* - working SM data;
* - card operations related to SM in 'APDU TRANSMIT' mode;
* - external SM module;
* - 'lock'/'unlock' handlers to allow SM transfer in the locked card session.
*/
typedef struct sm_context {
char config_section[64];
unsigned sm_mode, sm_flags;
struct sm_info info;
struct sm_card_operations ops;
struct sm_module module;
unsigned long (*app_lock)(void);
void (*app_unlock)(void);
} sm_context_t;
int iasecc_sm_external_authentication(struct sc_card *, unsigned, int *);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -267,6 +267,8 @@ typedef struct sc_apdu {
u8 control; /* Set if APDU should go to the reader */
unsigned int sw1, sw2; /* Status words returned in R-APDU */
unsigned char mac[8];
size_t mac_len;
unsigned long flags;

17
src/sm/Makefile.am Normal file
View File

@ -0,0 +1,17 @@
# Process this file with automake to create Makefile.in
MAINTAINERCLEANFILES = Makefile.in
EXTRA_DIST = Makefile.mak
AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_READLINE_CFLAGS)
INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/common -I$(top_builddir)/src/include
LIBS = $(top_builddir)/src/libopensc/libopensc.la $(top_builddir)/src/common/libcompat.la
lib_LTLIBRARIES = libsmm-local.la
libsmm_local_la_SOURCES =
libsmm_local_la_LIBADD = ../libopensc/libopensc.la
libsmm_local_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@
# noinst_HEADERS = sm.h