SM: move SM APDU procedures to dedicated source file

new SM errors: 'session-already-opened' and 'invalid-checksum'
declare typed data for DH SM session
This commit is contained in:
Viktor Tarasov 2013-01-06 16:38:33 +01:00
parent 4c1c39f3e4
commit 8d7c773561
8 changed files with 240 additions and 156 deletions

View File

@ -46,7 +46,7 @@ libopensc_la_SOURCES = \
pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \ pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \
pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c pkcs15-oberthur.c \ pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c pkcs15-oberthur.c \
pkcs15-itacns.c pkcs15-gemsafeV1.c pkcs15-sc-hsm.c \ pkcs15-itacns.c pkcs15-gemsafeV1.c pkcs15-sc-hsm.c \
compression.c p15card-helper.c \ compression.c p15card-helper.c sm.c \
libopensc.exports libopensc.exports
if WIN32 if WIN32
libopensc_la_SOURCES += $(top_builddir)/win32/versioninfo.rc libopensc_la_SOURCES += $(top_builddir)/win32/versioninfo.rc

View File

@ -30,7 +30,7 @@ OBJECTS = \
pkcs15-actalis.obj pkcs15-atrust-acos.obj pkcs15-tccardos.obj pkcs15-piv.obj \ pkcs15-actalis.obj pkcs15-atrust-acos.obj pkcs15-tccardos.obj pkcs15-piv.obj \
pkcs15-esinit.obj pkcs15-westcos.obj pkcs15-pteid.obj pkcs15-oberthur.obj \ pkcs15-esinit.obj pkcs15-westcos.obj pkcs15-pteid.obj pkcs15-oberthur.obj \
pkcs15-itacns.obj pkcs15-gemsafeV1.obj pkcs15-sc-hsm.obj \ pkcs15-itacns.obj pkcs15-gemsafeV1.obj pkcs15-sc-hsm.obj \
compression.obj p15card-helper.obj \ compression.obj p15card-helper.obj sm.obj \
$(TOPDIR)\win32\versioninfo.res $(TOPDIR)\win32\versioninfo.res
all: $(TOPDIR)\win32\versioninfo.res $(TARGET) all: $(TOPDIR)\win32\versioninfo.res $(TARGET)

View File

@ -234,95 +234,6 @@ int sc_apdu_set_resp(sc_context_t *ctx, sc_apdu_t *apdu, const u8 *buf,
return SC_SUCCESS; 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 */ /* higher level APDU transfer handling functions */
@ -354,7 +265,8 @@ sc_sm_update_apdu_response(struct sc_card *card, unsigned char *resp_data, size_
* @param apdu sc_apdu_t object to check * @param apdu sc_apdu_t object to check
* @return SC_SUCCESS on success and an error code otherwise * @return SC_SUCCESS on success and an error code otherwise
*/ */
static int sc_check_apdu(sc_card_t *card, const sc_apdu_t *apdu) int
sc_check_apdu(sc_card_t *card, const sc_apdu_t *apdu)
{ {
if ((apdu->cse & ~SC_APDU_SHORT_MASK) == 0) { if ((apdu->cse & ~SC_APDU_SHORT_MASK) == 0) {
/* length check for short APDU */ /* length check for short APDU */
@ -441,7 +353,8 @@ error:
* 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 * @param apdu APDU object
*/ */
static void sc_detect_apdu_cse(const sc_card_t *card, sc_apdu_t *apdu) static void
sc_detect_apdu_cse(const sc_card_t *card, sc_apdu_t *apdu)
{ {
if (apdu->cse == SC_APDU_CASE_2 || apdu->cse == SC_APDU_CASE_3 || if (apdu->cse == SC_APDU_CASE_2 || apdu->cse == SC_APDU_CASE_3 ||
apdu->cse == SC_APDU_CASE_4) { apdu->cse == SC_APDU_CASE_4) {
@ -458,48 +371,6 @@ static void sc_detect_apdu_cse(const sc_card_t *card, sc_apdu_t *apdu)
} }
#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);
if (rv == SC_ERROR_SM_NOT_APPLIED) {
/* SM wrap of this APDU is ignored by card driver.
* Send plain APDU to the reader driver */
rv = card->reader->ops->transmit(card->reader, apdu);
LOG_FUNC_RETURN(ctx, rv);
}
LOG_TEST_RET(ctx, rv, "get SM APDU error");
/* 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 static int
sc_single_transmit(struct sc_card *card, struct sc_apdu *apdu) sc_single_transmit(struct sc_card *card, struct sc_apdu *apdu)
{ {
@ -514,7 +385,7 @@ sc_single_transmit(struct sc_card *card, struct sc_apdu *apdu)
apdu->cla, apdu->ins, apdu->p1, apdu->p2, apdu->datalen, apdu->data); apdu->cla, apdu->ins, apdu->p1, apdu->p2, apdu->datalen, apdu->data);
#ifdef ENABLE_SM #ifdef ENABLE_SM
if (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT) if (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT)
return sc_single_sm_transmit(card, apdu); return sc_sm_single_transmit(card, apdu);
#endif #endif
/* send APDU to the reader driver */ /* send APDU to the reader driver */
@ -524,6 +395,7 @@ sc_single_transmit(struct sc_card *card, struct sc_apdu *apdu)
LOG_FUNC_RETURN(ctx, rv); LOG_FUNC_RETURN(ctx, rv);
} }
static int static int
sc_set_le_and_transmit(struct sc_card *card, struct sc_apdu *apdu, size_t olen) sc_set_le_and_transmit(struct sc_card *card, struct sc_apdu *apdu, size_t olen)
{ {

View File

@ -120,7 +120,6 @@ const char *sc_strerror(int error)
}; };
const int p15i_base = -SC_ERROR_PKCS15INIT; const int p15i_base = -SC_ERROR_PKCS15INIT;
const int sm_base = -SC_ERROR_SM;
const char *sm_errors[] = { const char *sm_errors[] = {
"Generic Secure Messaging error", "Generic Secure Messaging error",
"Data enciphering error", "Data enciphering error",
@ -131,9 +130,12 @@ const char *sc_strerror(int error)
"Cannot authenticate card", "Cannot authenticate card",
"Random generation error", "Random generation error",
"Secure messaging keyset not found", "Secure messaging keyset not found",
"IFD data missing" "IFD data missing",
"SM not applied",
"SM session already active",
"Invalid checksum"
}; };
const int sm_base = -SC_ERROR_SM;
const char *misc_errors[] = { const char *misc_errors[] = {
"Unknown error", "Unknown error",

View File

@ -110,17 +110,19 @@ extern "C" {
#define SC_ERROR_FILE_TOO_SMALL -1510 #define SC_ERROR_FILE_TOO_SMALL -1510
/* Related to secure messaging */ /* Related to secure messaging */
#define SC_ERROR_SM -1600 #define SC_ERROR_SM -1600
#define SC_ERROR_SM_ENCRYPT_FAILED -1601 #define SC_ERROR_SM_ENCRYPT_FAILED -1601
#define SC_ERROR_SM_INVALID_LEVEL -1602 #define SC_ERROR_SM_INVALID_LEVEL -1602
#define SC_ERROR_SM_NO_SESSION_KEYS -1603 #define SC_ERROR_SM_NO_SESSION_KEYS -1603
#define SC_ERROR_SM_INVALID_SESSION_KEY -1604 #define SC_ERROR_SM_INVALID_SESSION_KEY -1604
#define SC_ERROR_SM_NOT_INITIALIZED -1605 #define SC_ERROR_SM_NOT_INITIALIZED -1605
#define SC_ERROR_SM_AUTHENTICATION_FAILED -1606 #define SC_ERROR_SM_AUTHENTICATION_FAILED -1606
#define SC_ERROR_SM_RAND_FAILED -1607 #define SC_ERROR_SM_RAND_FAILED -1607
#define SC_ERROR_SM_KEYSET_NOT_FOUND -1608 #define SC_ERROR_SM_KEYSET_NOT_FOUND -1608
#define SC_ERROR_SM_IFD_DATA_MISSING -1609 #define SC_ERROR_SM_IFD_DATA_MISSING -1609
#define SC_ERROR_SM_NOT_APPLIED -1610 #define SC_ERROR_SM_NOT_APPLIED -1610
#define SC_ERROR_SM_SESSION_ALREADY_ACTIVE -1611
#define SC_ERROR_SM_INVALID_CHECKSUM -1612
/* Errors that do not fit the categories above */ /* Errors that do not fit the categories above */

View File

@ -306,6 +306,9 @@ sc_remote_data_init
sc_crc32 sc_crc32
sc_pkcs15_convert_prkey sc_pkcs15_convert_prkey
sc_pkcs15_convert_pubkey sc_pkcs15_convert_pubkey
sc_sm_parse_answer
sc_sm_update_apdu_response
sc_sm_single_transmit
iasecc_sdo_encode_update_field iasecc_sdo_encode_update_field
iasecc_sm_create_file iasecc_sm_create_file
iasecc_sm_delete_file iasecc_sm_delete_file

179
src/libopensc/sm.c Normal file
View File

@ -0,0 +1,179 @@
/*
* sm.c: Secure Messaging helper functions
*
* Copyright (C) 2013 Viktor Tarasov <viktor.tarasov@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 "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "internal.h"
#include "asn1.h"
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 }
};
#ifdef ENABLE_SM
int
sc_sm_parse_answer(struct sc_card *card, 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 rv;
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);
rv = sc_asn1_decode(card->ctx, asn1_sm_response, resp_data, resp_len, NULL, NULL);
if (rv)
return rv;
if (asn1_sm_response[0].flags & SC_ASN1_PRESENT) {
if (data_len > sizeof(out->data))
return SC_ERROR_BUFFER_TOO_SMALL;
memcpy(out->data, data, data_len);
out->data_len = data_len;
}
if (asn1_sm_response[1].flags & SC_ASN1_PRESENT) {
if (!status[0])
return SC_ERROR_INVALID_DATA;
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;
}
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
*/
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, resp_data, resp_len, &sm_resp);
if (r)
return r;
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;
}
int
sc_sm_single_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);
if (rv == SC_ERROR_SM_NOT_APPLIED) {
/* SM wrap of this APDU is ignored by card driver.
* Send plain APDU to the reader driver */
rv = card->reader->ops->transmit(card->reader, apdu);
LOG_FUNC_RETURN(ctx, rv);
}
LOG_TEST_RET(ctx, rv, "get SM APDU error");
/* 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);
}
#else
int
sc_sm_parse_answer(struct sc_context *ctx, unsigned char *resp_data, size_t resp_len,
struct sm_card_response *out)
{
return SC_ERROR_NOT_SUPPORTED;
}
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)
{
return SC_ERROR_NOT_SUPPORTED;
}
int
sc_sm_single_transmit(struct sc_card *card, struct sc_apdu *apdu)
{
return SC_ERROR_NOT_SUPPORTED;
}
#endif

View File

@ -43,21 +43,22 @@ extern "C" {
#define SM_TYPE_GP_SCP01 0x100 #define SM_TYPE_GP_SCP01 0x100
#define SM_TYPE_CWA14890 0x400 #define SM_TYPE_CWA14890 0x400
#define SM_TYPE_DH_RSA 0x500
#define SM_MODE_NONE 0x0 #define SM_MODE_NONE 0x0
#define SM_MODE_ACL 0x100 #define SM_MODE_ACL 0x100
#define SM_MODE_TRANSMIT 0x200 #define SM_MODE_TRANSMIT 0x200
#define SM_CMD_INITIALIZE 0x10 #define SM_CMD_INITIALIZE 0x10
#define SM_CMD_MUTUAL_AUTHENTICATION 0x20 #define SM_CMD_MUTUAL_AUTHENTICATION 0x20
#define SM_CMD_RSA 0x100 #define SM_CMD_RSA 0x100
#define SM_CMD_RSA_GENERATE 0x101 #define SM_CMD_RSA_GENERATE 0x101
#define SM_CMD_RSA_UPDATE 0x102 #define SM_CMD_RSA_UPDATE 0x102
#define SM_CMD_RSA_READ_PUBLIC 0x103 #define SM_CMD_RSA_READ_PUBLIC 0x103
#define SM_CMD_FILE 0x200 #define SM_CMD_FILE 0x200
#define SM_CMD_FILE_READ 0x201 #define SM_CMD_FILE_READ 0x201
#define SM_CMD_FILE_UPDATE 0x202 #define SM_CMD_FILE_UPDATE 0x202
#define SM_CMD_FILE_CREATE 0x203 #define SM_CMD_FILE_CREATE 0x203
#define SM_CMD_FILE_DELETE 0x204 #define SM_CMD_FILE_DELETE 0x204
#define SM_CMD_PIN 0x300 #define SM_CMD_PIN 0x300
#define SM_CMD_PIN_VERIFY 0x301 #define SM_CMD_PIN_VERIFY 0x301
@ -200,6 +201,26 @@ struct sm_cwa_session {
size_t mdata_len; size_t mdata_len;
}; };
/*
* @struct sm_dh_session
* DH SM session data:
*/
struct sm_dh_session {
struct sc_tlv_data g;
struct sc_tlv_data N;
struct sc_tlv_data ifd_p;
struct sc_tlv_data ifd_y;
struct sc_tlv_data icc_p;
struct sc_tlv_data shared_secret;
unsigned char session_enc[16];
unsigned char session_mac[16];
unsigned char card_challenge[32];
unsigned char ssc[8];
};
/* /*
* @struct sc_info is the * @struct sc_info is the
* placehold for the secure messaging working data: * placehold for the secure messaging working data:
@ -219,6 +240,7 @@ struct sm_info {
union { union {
struct sm_gp_session gp; struct sm_gp_session gp;
struct sm_cwa_session cwa; struct sm_cwa_session cwa;
struct sm_dh_session dh;
} session; } session;
struct sc_serial_number serialnr; struct sc_serial_number serialnr;
@ -329,6 +351,10 @@ typedef struct sm_context {
int iasecc_sm_external_authentication(struct sc_card *, unsigned, int *); int iasecc_sm_external_authentication(struct sc_card *, unsigned, int *);
int sc_sm_parse_answer(struct sc_card *, unsigned char *, size_t, struct sm_card_response *);
int sc_sm_update_apdu_response(struct sc_card *, unsigned char *, size_t, int, struct sc_apdu *);
int sc_sm_single_transmit(struct sc_card *, struct sc_apdu *);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif