/* * boxing.c: implementation related to boxing commands with pseudo APDUs * * Copyright (C) 2013 Frank Morgner * * This file is part of libnpa. * * libnpa is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * libnpa is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * libnpa. If not, see . */ #include "ccid-types.h" #include #include #include #include #include #include #include #if _WIN32 /* FIXME might not always work */ #define htole16(x) (x) #define htole32(x) (x) #else #ifndef _BSD_SOURCE #define _BSD_SOURCE /* See feature_test_macros(7) */ #endif #include #endif #if HAVE_SC_APDU_GET_OCTETS #include "libopensc/internal.h" #else /* Pull request for exporting sc_apdu_get_octets is pending. */ #include "libopensc/apdu.c" #endif #ifndef HAVE_LIST_ITERATOR_NEXT #include "opensc/src/common/simclist.c" #endif static const u8 boxing_cla = 0xff; static const u8 boxing_ins = 0x9a; static const u8 boxing_p1 = 0x04; static const u8 boxing_p2_GetReaderPACECapabilities = 0x01; static const u8 boxing_p2_EstablishPACEChannel = 0x02; static const u8 boxing_p2_DestroyPACEChannel = 0x03; static const u8 boxing_p2_PC_to_RDR_Secure = 0x10; 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) { size_t pin_id_len = sizeof input->pin_id; struct sc_asn1_entry EstablishPACEChannelInput_data[ sizeof g_EstablishPACEChannelInput_data/ sizeof *g_EstablishPACEChannelInput_data]; struct sc_asn1_entry EstablishPACEChannel[ sizeof g_EstablishPACEChannel/ sizeof *g_EstablishPACEChannel]; sc_copy_asn1_entry(g_EstablishPACEChannel, EstablishPACEChannel); sc_format_asn1_entry(EstablishPACEChannel, EstablishPACEChannelInput_data, 0, 1); sc_copy_asn1_entry(g_EstablishPACEChannelInput_data, EstablishPACEChannelInput_data); sc_format_asn1_entry(EstablishPACEChannelInput_data+0, (unsigned char *) &input->pin_id, &pin_id_len, 1); if (input->pin) sc_format_asn1_entry(EstablishPACEChannelInput_data+1, (unsigned char *) input->pin, (size_t *) &input->pin_length, 1); if (input->chat) sc_format_asn1_entry(EstablishPACEChannelInput_data+2, (unsigned char *) input->chat, (size_t *) &input->chat_length, 1); if (input->certificate_description) sc_format_asn1_entry(EstablishPACEChannelInput_data+3, (unsigned char *) input->certificate_description, (size_t *) &input->certificate_description_length, 1); return sc_asn1_encode(ctx, EstablishPACEChannel, asn1, asn1_len); } int boxing_buf_to_pace_input(sc_context_t *ctx, const unsigned char *asn1, size_t asn1_len, struct establish_pace_channel_input *input) { size_t pin_id_len = sizeof input->pin_id; struct sc_asn1_entry EstablishPACEChannelInput_data[ sizeof g_EstablishPACEChannelInput_data/ sizeof *g_EstablishPACEChannelInput_data]; struct sc_asn1_entry EstablishPACEChannel[ sizeof g_EstablishPACEChannel/ sizeof *g_EstablishPACEChannel]; sc_copy_asn1_entry(g_EstablishPACEChannel, EstablishPACEChannel); sc_format_asn1_entry(EstablishPACEChannel, EstablishPACEChannelInput_data, 0, 0); sc_copy_asn1_entry(g_EstablishPACEChannelInput_data, EstablishPACEChannelInput_data); sc_format_asn1_entry(EstablishPACEChannelInput_data+0, &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) { uint16_t status_mse_set_at = ((output->mse_set_at_sw1 & 0xff) << 8) | output->mse_set_at_sw2; size_t result_len = sizeof output->result, status_mse_set_at_len = sizeof status_mse_set_at; struct sc_asn1_entry EstablishPACEChannelOutput_data[ sizeof g_EstablishPACEChannelOutput_data/ sizeof *g_EstablishPACEChannelOutput_data]; struct sc_asn1_entry EstablishPACEChannel[ sizeof g_EstablishPACEChannel/ sizeof *g_EstablishPACEChannel]; sc_copy_asn1_entry(g_EstablishPACEChannel, EstablishPACEChannel); sc_format_asn1_entry(EstablishPACEChannel, EstablishPACEChannelOutput_data, 0, 1); sc_copy_asn1_entry(g_EstablishPACEChannelOutput_data, EstablishPACEChannelOutput_data); sc_format_asn1_entry(EstablishPACEChannelOutput_data+0, (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) { uint16_t status_mse_set_at; size_t result_len = sizeof output->result, status_mse_set_at_len = sizeof status_mse_set_at; struct sc_asn1_entry EstablishPACEChannelOutput_data[ sizeof g_EstablishPACEChannelOutput_data/ sizeof *g_EstablishPACEChannelOutput_data]; struct sc_asn1_entry EstablishPACEChannel[ sizeof g_EstablishPACEChannel/ sizeof *g_EstablishPACEChannel]; sc_copy_asn1_entry(g_EstablishPACEChannel, EstablishPACEChannel); sc_format_asn1_entry(EstablishPACEChannel, EstablishPACEChannelOutput_data, 0, 0); sc_copy_asn1_entry(g_EstablishPACEChannelOutput_data, EstablishPACEChannelOutput_data); sc_format_asn1_entry(EstablishPACEChannelOutput_data+0, &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; } #define CCID_PIN_TIMEOUT 30 #define CCID_DISPLAY_DEFAULT 0xff static int boxing_pin_cmd_to_buf(sc_context_t *ctx, const struct sc_pin_cmd_data *data, unsigned char **pc_to_rdr_secure, size_t *pc_to_rdr_secure_len) { PC_to_RDR_Secure_t *secure; abPINDataStucture_Modification_t *modify; abPINDataStucture_Verification_t *verify; uint16_t wLangId = 0, bTeoPrologue2 = 0, wPINMaxExtraDigit; uint8_t bTimeOut = CCID_PIN_TIMEOUT, bNumberMessage = CCID_DISPLAY_DEFAULT, bTeoPrologue1 = 0, bMsgIndex = 0, bMessageType = 0x69, bSlot = 0, bSeq = 0, bBWI = 0xff, wLevelParameter = 0, bEntryValidationCondition = CCID_ENTRY_VALIDATE, bmFormatString, bmPINLengthFormat, bmPINBlockString; const struct sc_pin_cmd_pin *pin_ref; int r; unsigned char *pinapdu = NULL; size_t pinapdu_len = 0; if (!data || !pc_to_rdr_secure || !pc_to_rdr_secure_len) { r = SC_ERROR_INVALID_ARGUMENTS; goto err; } pin_ref = data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ? &data->pin2 : &data->pin1; wPINMaxExtraDigit = htole16( (0xff & pin_ref->min_length) << 8) | (pin_ref->max_length & 0xff); bmFormatString = CCID_PIN_UNITS_BYTES | ((pin_ref->offset & 0xf) << 3); switch (pin_ref->encoding) { case SC_PIN_ENCODING_ASCII: bmFormatString |= CCID_PIN_ENCODING_ASCII; break; case SC_PIN_ENCODING_BCD: bmFormatString |= CCID_PIN_ENCODING_BCD; break; default: r = SC_ERROR_INVALID_ARGUMENTS; goto err; } /* GLP PINs expect the effective PIN length from bit 4 */ bmPINLengthFormat = pin_ref->encoding == SC_PIN_ENCODING_GLP ? 0x04 : 0x00; if (pin_ref->encoding == SC_PIN_ENCODING_GLP) { /* GLP PIN length is encoded in 4 bits and block size is always 8 bytes */ bmPINBlockString = 0x40 | 0x08; } else if (pin_ref->encoding == SC_PIN_ENCODING_ASCII && data->flags & SC_PIN_CMD_NEED_PADDING) { bmPINBlockString = pin_ref->pad_length; } else { bmPINBlockString = 0x00; } r = sc_apdu_get_octets(ctx, data->apdu, &pinapdu, &pinapdu_len, SC_PROTO_T1); if (r < 0) goto err; switch (data->cmd) { case SC_PIN_CMD_VERIFY: *pc_to_rdr_secure_len = sizeof *secure + 1 + sizeof *verify + pinapdu_len; break; case SC_PIN_CMD_CHANGE: *pc_to_rdr_secure_len = sizeof *secure + 1 + sizeof *modify + 3 + pinapdu_len; break; default: r = SC_ERROR_INVALID_ARGUMENTS; goto err; } *pc_to_rdr_secure = malloc(*pc_to_rdr_secure_len); if (!*pc_to_rdr_secure) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } secure = (PC_to_RDR_Secure_t *) *pc_to_rdr_secure; secure->bMessageType = bMessageType; secure->dwLength = htole32((*pc_to_rdr_secure_len) - sizeof *secure); secure->bSlot = bSlot; secure->bSeq = bSeq; secure->bBWI = bBWI; secure->wLevelParameter = wLevelParameter; switch (data->cmd) { case SC_PIN_CMD_VERIFY: /* bPINOperation */ *((*pc_to_rdr_secure) + sizeof *secure) = CCID_OPERATION_VERIFY; verify = (abPINDataStucture_Verification_t *) ((*pc_to_rdr_secure) + sizeof *secure + 1); verify->bTimeOut = bTimeOut; verify->bmFormatString = bmFormatString; verify->bmPINBlockString = bmPINBlockString; verify->bmPINLengthFormat = bmPINLengthFormat; verify->wPINMaxExtraDigit = wPINMaxExtraDigit; verify->bEntryValidationCondition = bEntryValidationCondition; verify->bNumberMessage = bNumberMessage; verify->wLangId = wLangId; verify->bMsgIndex = bMsgIndex; verify->bTeoPrologue1 = bTeoPrologue1; verify->bTeoPrologue2 = bTeoPrologue2; memcpy((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *verify, pinapdu, pinapdu_len); break; case SC_PIN_CMD_CHANGE: /* bPINOperation */ *((*pc_to_rdr_secure) + sizeof *secure) = CCID_OPERATION_MODIFY; modify = (abPINDataStucture_Modification_t *) ((*pc_to_rdr_secure) + sizeof *secure + 1); modify->bTimeOut = bTimeOut; modify->bmFormatString = bmFormatString; modify->bmPINBlockString = bmPINBlockString; modify->bmPINLengthFormat = bmPINLengthFormat; if (!(data->flags & SC_PIN_CMD_IMPLICIT_CHANGE) && data->pin1.offset) { modify->bInsertionOffsetOld = data->pin1.offset - 5; } else { modify->bInsertionOffsetOld = 0; } modify->bInsertionOffsetNew = data->pin2.offset ? data->pin2.offset - 5 : 0; modify->wPINMaxExtraDigit = wPINMaxExtraDigit; modify->bConfirmPIN = CCID_PIN_CONFIRM_NEW | (data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ? 0 : CCID_PIN_INSERT_OLD); modify->bEntryValidationCondition = bEntryValidationCondition; modify->bNumberMessage = bNumberMessage; modify->wLangId = wLangId; modify->bMsgIndex1 = bMsgIndex; *((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 0) = bTeoPrologue1; *((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 1) = bTeoPrologue1; *((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 2) = bTeoPrologue1; memcpy((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 3, pinapdu, pinapdu_len); break; default: r = SC_ERROR_INVALID_ARGUMENTS; goto err; } r = SC_SUCCESS; err: free(pinapdu); if (r < 0 && *pc_to_rdr_secure) { free(*pc_to_rdr_secure); *pc_to_rdr_secure = NULL; } return r; } #define CCID_BSTATUS_OK_ACTIVE 0x00 /** No error. An ICC is present and active */ static int boxing_buf_to_verify_result(sc_context_t *ctx, const unsigned char *rdr_to_pc_datablock, size_t rdr_to_pc_datablock_len, sc_apdu_t *apdu) { RDR_to_PC_DataBlock_t *datablock = (RDR_to_PC_DataBlock_t *) rdr_to_pc_datablock; if (!rdr_to_pc_datablock || rdr_to_pc_datablock_len < sizeof *datablock || datablock->bMessageType != 0x80) return SC_ERROR_UNKNOWN_DATA_RECEIVED; if (datablock->bStatus != CCID_BSTATUS_OK_ACTIVE) return SC_ERROR_TRANSMIT_FAILED; return sc_apdu_set_resp(ctx, apdu, rdr_to_pc_datablock + sizeof *datablock, htole32(datablock->dwLength)); } static int boxing_perform_verify(struct sc_reader *reader, struct sc_pin_cmd_data *data) { u8 rbuf[0xff]; sc_apdu_t apdu; int r; memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_4_SHORT; apdu.cla = boxing_cla; apdu.ins = boxing_ins; apdu.p1 = boxing_p1; apdu.p2 = boxing_p2_PC_to_RDR_Secure; apdu.resp = rbuf; apdu.resplen = sizeof rbuf; apdu.le = sizeof rbuf; if (!reader || !reader->ops || !reader->ops->transmit) { r = SC_ERROR_NOT_SUPPORTED; goto err; } r = boxing_pin_cmd_to_buf(reader->ctx, data, (unsigned char **) &apdu.data, &apdu.datalen); if (r < 0) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error encoding PC_to_RDR_Secure"); goto err; } apdu.lc = apdu.datalen; r = SC_SUCCESS; r = reader->ops->transmit(reader, &apdu); if (r < 0) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error performing PC_to_RDR_Secure"); goto err; } if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error decoding PC_to_RDR_Secure"); r = SC_ERROR_NOT_SUPPORTED; goto err; } r = boxing_buf_to_verify_result(reader->ctx, apdu.resp, apdu.resplen, data->apdu); err: free((unsigned char *) apdu.data); return r; } static int boxing_perform_pace(struct sc_reader *reader, void *establish_pace_channel_input, void *establish_pace_channel_output) { u8 rbuf[0xffff]; sc_apdu_t apdu; int r; struct establish_pace_channel_input *input = establish_pace_channel_input; struct establish_pace_channel_output *output = establish_pace_channel_output; memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_4_EXT; apdu.cla = boxing_cla; apdu.ins = boxing_ins; apdu.p1 = boxing_p1; apdu.p2 = boxing_p2_EstablishPACEChannel; apdu.resp = rbuf; apdu.resplen = sizeof rbuf; apdu.le = sizeof rbuf; if (!reader || !reader->ops || !reader->ops->transmit) { r = SC_ERROR_NOT_SUPPORTED; goto err; } r = boxing_pace_input_to_buf(reader->ctx, input, (unsigned char **) &apdu.data, &apdu.datalen); if (r < 0) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error encoding EstablishPACEChannel"); goto err; } apdu.lc = apdu.datalen; r = reader->ops->transmit(reader, &apdu); if (r < 0) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error performing EstablishPACEChannel"); goto err; } if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) { sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "Error decoding EstablishPACEChannel"); r = SC_ERROR_NOT_SUPPORTED; goto err; } r = boxing_buf_to_pace_output(reader->ctx, apdu.resp, apdu.resplen, output); err: free((unsigned char *) apdu.data); return r; } struct sc_asn1_entry g_PACECapabilities_data[] = { { "capabilityPACE", SC_ASN1_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) { int capabilityPACE = 0, capabilityEID = 0, capabilityESign = 0, capabilityDestroy = 0; struct sc_asn1_entry PACECapabilities_data[ sizeof g_PACECapabilities_data/ sizeof *g_PACECapabilities_data]; struct sc_asn1_entry PACECapabilities[ sizeof g_PACECapabilities/ sizeof *g_PACECapabilities]; sc_copy_asn1_entry(g_PACECapabilities, PACECapabilities); sc_format_asn1_entry(PACECapabilities, PACECapabilities_data, 0, 1); sc_copy_asn1_entry(g_PACECapabilities_data, PACECapabilities_data); sc_format_asn1_entry(PACECapabilities_data+0, &capabilityPACE, NULL, 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) { int yes = 1, no = 0; struct sc_asn1_entry PACECapabilities_data[ sizeof g_PACECapabilities_data/ sizeof *g_PACECapabilities_data]; struct sc_asn1_entry PACECapabilities[ sizeof g_PACECapabilities/ sizeof *g_PACECapabilities]; sc_copy_asn1_entry(g_EstablishPACEChannel, PACECapabilities); sc_format_asn1_entry(PACECapabilities, PACECapabilities_data, 0, 1); sc_copy_asn1_entry(g_PACECapabilities_data, PACECapabilities_data); 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); } #ifndef DISABLE_GLOBAL_BOXING_INITIALIZATION static #endif void sc_detect_boxing_cmds(sc_reader_t *reader) { u8 rbuf[0xff]; sc_apdu_t apdu; unsigned long capabilities; memset(&apdu, 0, sizeof(apdu)); apdu.cse = SC_APDU_CASE_1; apdu.cla = boxing_cla; apdu.ins = boxing_ins; apdu.p1 = boxing_p1; apdu.p2 = boxing_p2_GetReaderPACECapabilities; apdu.resp = rbuf; apdu.resplen = sizeof rbuf; apdu.le = sizeof rbuf; if (!reader || !reader->ops || !reader->ops->transmit || reader->ops->transmit(reader, &apdu) != SC_SUCCESS || apdu.sw1 != 0x90 || apdu.sw2 != 0x00 || boxing_buf_to_pace_capabilities(reader->ctx, apdu.resp, apdu.resplen, &capabilities) != SC_SUCCESS) { 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; } } #ifndef 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