From 8d7c773561e3359154b488456a30f97fbed10914 Mon Sep 17 00:00:00 2001 From: Viktor Tarasov Date: Sun, 6 Jan 2013 16:38:33 +0100 Subject: [PATCH] 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 --- src/libopensc/Makefile.am | 2 +- src/libopensc/Makefile.mak | 2 +- src/libopensc/apdu.c | 140 ++----------------------- src/libopensc/errors.c | 8 +- src/libopensc/errors.h | 24 +++-- src/libopensc/libopensc.exports | 3 + src/libopensc/sm.c | 179 ++++++++++++++++++++++++++++++++ src/libopensc/sm.h | 38 +++++-- 8 files changed, 240 insertions(+), 156 deletions(-) create mode 100644 src/libopensc/sm.c diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 538b1677..69a977db 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -46,7 +46,7 @@ libopensc_la_SOURCES = \ 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-itacns.c pkcs15-gemsafeV1.c pkcs15-sc-hsm.c \ - compression.c p15card-helper.c \ + compression.c p15card-helper.c sm.c \ libopensc.exports if WIN32 libopensc_la_SOURCES += $(top_builddir)/win32/versioninfo.rc diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak index 9902f871..d604c7d5 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -30,7 +30,7 @@ OBJECTS = \ 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-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 all: $(TOPDIR)\win32\versioninfo.res $(TARGET) diff --git a/src/libopensc/apdu.c b/src/libopensc/apdu.c index 4cbd53d1..55f13d08 100644 --- a/src/libopensc/apdu.c +++ b/src/libopensc/apdu.c @@ -234,95 +234,6 @@ 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 */ @@ -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 * @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) { /* length check for short APDU */ @@ -441,7 +353,8 @@ error: * 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) +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 || 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 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); #ifdef ENABLE_SM if (card->sm_ctx.sm_mode == SM_MODE_TRANSMIT) - return sc_single_sm_transmit(card, apdu); + return sc_sm_single_transmit(card, apdu); #endif /* 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); } + static int sc_set_le_and_transmit(struct sc_card *card, struct sc_apdu *apdu, size_t olen) { diff --git a/src/libopensc/errors.c b/src/libopensc/errors.c index 9098b35b..adc55b87 100644 --- a/src/libopensc/errors.c +++ b/src/libopensc/errors.c @@ -120,7 +120,6 @@ const char *sc_strerror(int error) }; const int p15i_base = -SC_ERROR_PKCS15INIT; - const int sm_base = -SC_ERROR_SM; const char *sm_errors[] = { "Generic Secure Messaging error", "Data enciphering error", @@ -131,9 +130,12 @@ const char *sc_strerror(int error) "Cannot authenticate card", "Random generation error", "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[] = { "Unknown error", diff --git a/src/libopensc/errors.h b/src/libopensc/errors.h index 579de685..92403144 100644 --- a/src/libopensc/errors.h +++ b/src/libopensc/errors.h @@ -110,17 +110,19 @@ extern "C" { #define SC_ERROR_FILE_TOO_SMALL -1510 /* Related to secure messaging */ -#define SC_ERROR_SM -1600 -#define SC_ERROR_SM_ENCRYPT_FAILED -1601 -#define SC_ERROR_SM_INVALID_LEVEL -1602 -#define SC_ERROR_SM_NO_SESSION_KEYS -1603 -#define SC_ERROR_SM_INVALID_SESSION_KEY -1604 -#define SC_ERROR_SM_NOT_INITIALIZED -1605 -#define SC_ERROR_SM_AUTHENTICATION_FAILED -1606 -#define SC_ERROR_SM_RAND_FAILED -1607 -#define SC_ERROR_SM_KEYSET_NOT_FOUND -1608 -#define SC_ERROR_SM_IFD_DATA_MISSING -1609 -#define SC_ERROR_SM_NOT_APPLIED -1610 +#define SC_ERROR_SM -1600 +#define SC_ERROR_SM_ENCRYPT_FAILED -1601 +#define SC_ERROR_SM_INVALID_LEVEL -1602 +#define SC_ERROR_SM_NO_SESSION_KEYS -1603 +#define SC_ERROR_SM_INVALID_SESSION_KEY -1604 +#define SC_ERROR_SM_NOT_INITIALIZED -1605 +#define SC_ERROR_SM_AUTHENTICATION_FAILED -1606 +#define SC_ERROR_SM_RAND_FAILED -1607 +#define SC_ERROR_SM_KEYSET_NOT_FOUND -1608 +#define SC_ERROR_SM_IFD_DATA_MISSING -1609 +#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 */ diff --git a/src/libopensc/libopensc.exports b/src/libopensc/libopensc.exports index fe118014..e677f129 100644 --- a/src/libopensc/libopensc.exports +++ b/src/libopensc/libopensc.exports @@ -306,6 +306,9 @@ sc_remote_data_init sc_crc32 sc_pkcs15_convert_prkey sc_pkcs15_convert_pubkey +sc_sm_parse_answer +sc_sm_update_apdu_response +sc_sm_single_transmit iasecc_sdo_encode_update_field iasecc_sm_create_file iasecc_sm_delete_file diff --git a/src/libopensc/sm.c b/src/libopensc/sm.c new file mode 100644 index 00000000..3b0d758b --- /dev/null +++ b/src/libopensc/sm.c @@ -0,0 +1,179 @@ +/* + * sm.c: Secure Messaging helper functions + * + * Copyright (C) 2013 Viktor Tarasov + * + * 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 +#include +#include +#include + +#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 diff --git a/src/libopensc/sm.h b/src/libopensc/sm.h index fe65bd42..d62e0c65 100644 --- a/src/libopensc/sm.h +++ b/src/libopensc/sm.h @@ -43,21 +43,22 @@ extern "C" { #define SM_TYPE_GP_SCP01 0x100 #define SM_TYPE_CWA14890 0x400 +#define SM_TYPE_DH_RSA 0x500 #define SM_MODE_NONE 0x0 #define SM_MODE_ACL 0x100 #define SM_MODE_TRANSMIT 0x200 -#define SM_CMD_INITIALIZE 0x10 +#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 0x100 +#define SM_CMD_RSA_GENERATE 0x101 #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_READ 0x201 -#define SM_CMD_FILE_UPDATE 0x202 -#define SM_CMD_FILE_CREATE 0x203 +#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 @@ -200,6 +201,26 @@ struct sm_cwa_session { 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 * placehold for the secure messaging working data: @@ -219,6 +240,7 @@ struct sm_info { union { struct sm_gp_session gp; struct sm_cwa_session cwa; + struct sm_dh_session dh; } session; struct sc_serial_number serialnr; @@ -329,6 +351,10 @@ typedef struct sm_context { 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 } #endif