Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
		
			
				
	
	
		
			756 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			756 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Implement the 7816 portion of the card spec
 | 
						|
 *
 | 
						|
 * This code is licensed under the GNU LGPL, version 2.1 or later.
 | 
						|
 * See the COPYING.LIB file in the top-level directory.
 | 
						|
 */
 | 
						|
 | 
						|
#include "qemu-common.h"
 | 
						|
 | 
						|
#include "vcard.h"
 | 
						|
#include "vcard_emul.h"
 | 
						|
#include "card_7816.h"
 | 
						|
 | 
						|
/*
 | 
						|
 * set the status bytes based on the status word
 | 
						|
 */
 | 
						|
static void
 | 
						|
vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status)
 | 
						|
{
 | 
						|
    unsigned char sw1, sw2;
 | 
						|
    response->b_status = status; /* make sure the status and swX representations
 | 
						|
                                  * are consistent */
 | 
						|
    sw1 = (status >> 8) & 0xff;
 | 
						|
    sw2 = status & 0xff;
 | 
						|
    response->b_sw1 = sw1;
 | 
						|
    response->b_sw2 = sw2;
 | 
						|
    response->b_data[response->b_len] = sw1;
 | 
						|
    response->b_data[response->b_len+1] = sw2;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * set the status bytes in a response buffer
 | 
						|
 */
 | 
						|
static void
 | 
						|
vcard_response_set_status_bytes(VCardResponse *response,
 | 
						|
                               unsigned char sw1, unsigned char sw2)
 | 
						|
{
 | 
						|
    response->b_status = sw1 << 8 | sw2;
 | 
						|
    response->b_sw1 = sw1;
 | 
						|
    response->b_sw2 = sw2;
 | 
						|
    response->b_data[response->b_len] = sw1;
 | 
						|
    response->b_data[response->b_len+1] = sw2;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * allocate a VCardResponse structure, plus space for the data buffer, and
 | 
						|
 * set up everything but the resonse bytes.
 | 
						|
 */
 | 
						|
VCardResponse *
 | 
						|
vcard_response_new_data(unsigned char *buf, int len)
 | 
						|
{
 | 
						|
    VCardResponse *new_response;
 | 
						|
 | 
						|
    new_response = g_new(VCardResponse, 1);
 | 
						|
    new_response->b_data = g_malloc(len + 2);
 | 
						|
    memcpy(new_response->b_data, buf, len);
 | 
						|
    new_response->b_total_len = len+2;
 | 
						|
    new_response->b_len = len;
 | 
						|
    new_response->b_type = VCARD_MALLOC;
 | 
						|
    return new_response;
 | 
						|
}
 | 
						|
 | 
						|
static VCardResponse *
 | 
						|
vcard_init_buffer_response(VCard *card, unsigned char *buf, int len)
 | 
						|
{
 | 
						|
    VCardResponse *response;
 | 
						|
    VCardBufferResponse *buffer_response;
 | 
						|
 | 
						|
    buffer_response = vcard_get_buffer_response(card);
 | 
						|
    if (buffer_response) {
 | 
						|
        vcard_set_buffer_response(card, NULL);
 | 
						|
        vcard_buffer_response_delete(buffer_response);
 | 
						|
    }
 | 
						|
    buffer_response = vcard_buffer_response_new(buf, len);
 | 
						|
    if (buffer_response == NULL) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    response = vcard_response_new_status_bytes(VCARD7816_SW1_RESPONSE_BYTES,
 | 
						|
                                               len > 255 ? 0 : len);
 | 
						|
    if (response == NULL) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    vcard_set_buffer_response(card, buffer_response);
 | 
						|
    return response;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * general buffer to hold results from APDU calls
 | 
						|
 */
 | 
						|
VCardResponse *
 | 
						|
vcard_response_new(VCard *card, unsigned char *buf,
 | 
						|
                   int len, int Le, vcard_7816_status_t status)
 | 
						|
{
 | 
						|
    VCardResponse *new_response;
 | 
						|
 | 
						|
    if (len > Le) {
 | 
						|
        return vcard_init_buffer_response(card, buf, len);
 | 
						|
    }
 | 
						|
    new_response = vcard_response_new_data(buf, len);
 | 
						|
    if (new_response == NULL) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    vcard_response_set_status(new_response, status);
 | 
						|
    return new_response;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * general buffer to hold results from APDU calls
 | 
						|
 */
 | 
						|
VCardResponse *
 | 
						|
vcard_response_new_bytes(VCard *card, unsigned char *buf, int len, int Le,
 | 
						|
                         unsigned char sw1, unsigned char sw2)
 | 
						|
{
 | 
						|
    VCardResponse *new_response;
 | 
						|
 | 
						|
    if (len > Le) {
 | 
						|
        return vcard_init_buffer_response(card, buf, len);
 | 
						|
    }
 | 
						|
    new_response = vcard_response_new_data(buf, len);
 | 
						|
    if (new_response == NULL) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    vcard_response_set_status_bytes(new_response, sw1, sw2);
 | 
						|
    return new_response;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * get a new Response buffer that only has a status.
 | 
						|
 */
 | 
						|
static VCardResponse *
 | 
						|
vcard_response_new_status(vcard_7816_status_t status)
 | 
						|
{
 | 
						|
    VCardResponse *new_response;
 | 
						|
 | 
						|
    new_response = g_new(VCardResponse, 1);
 | 
						|
    new_response->b_data = &new_response->b_sw1;
 | 
						|
    new_response->b_len = 0;
 | 
						|
    new_response->b_total_len = 2;
 | 
						|
    new_response->b_type = VCARD_MALLOC_STRUCT;
 | 
						|
    vcard_response_set_status(new_response, status);
 | 
						|
    return new_response;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * same as above, but specify the status as separate bytes
 | 
						|
 */
 | 
						|
VCardResponse *
 | 
						|
vcard_response_new_status_bytes(unsigned char sw1, unsigned char sw2)
 | 
						|
{
 | 
						|
    VCardResponse *new_response;
 | 
						|
 | 
						|
    new_response = g_new(VCardResponse, 1);
 | 
						|
    new_response->b_data = &new_response->b_sw1;
 | 
						|
    new_response->b_len = 0;
 | 
						|
    new_response->b_total_len = 2;
 | 
						|
    new_response->b_type = VCARD_MALLOC_STRUCT;
 | 
						|
    vcard_response_set_status_bytes(new_response, sw1, sw2);
 | 
						|
    return new_response;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * free the response buffer. The Buffer has a type to handle the buffer
 | 
						|
 * allocated in other ways than through malloc.
 | 
						|
 */
 | 
						|
void
 | 
						|
vcard_response_delete(VCardResponse *response)
 | 
						|
{
 | 
						|
    if (response == NULL) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    switch (response->b_type) {
 | 
						|
    case VCARD_MALLOC:
 | 
						|
        /* everything was malloc'ed */
 | 
						|
        g_free(response->b_data);
 | 
						|
        g_free(response);
 | 
						|
        break;
 | 
						|
    case VCARD_MALLOC_DATA:
 | 
						|
        /* only the data buffer was malloc'ed */
 | 
						|
        g_free(response->b_data);
 | 
						|
        break;
 | 
						|
    case VCARD_MALLOC_STRUCT:
 | 
						|
        /* only the structure was malloc'ed */
 | 
						|
        g_free(response);
 | 
						|
        break;
 | 
						|
    case VCARD_STATIC:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * decode the class bit and set our generic type field, channel, and
 | 
						|
 * secure messaging values.
 | 
						|
 */
 | 
						|
static vcard_7816_status_t
 | 
						|
vcard_apdu_set_class(VCardAPDU *apdu) {
 | 
						|
    apdu->a_channel = 0;
 | 
						|
    apdu->a_secure_messaging = 0;
 | 
						|
    apdu->a_type = apdu->a_cla & 0xf0;
 | 
						|
    apdu->a_gen_type = VCARD_7816_ISO;
 | 
						|
 | 
						|
    /* parse the class  tables 8 & 9 of the 7816-4 Part 4 spec */
 | 
						|
    switch (apdu->a_type) {
 | 
						|
        /* we only support the basic types */
 | 
						|
    case 0x00:
 | 
						|
    case 0x80:
 | 
						|
    case 0x90:
 | 
						|
    case 0xa0:
 | 
						|
        apdu->a_channel = apdu->a_cla & 3;
 | 
						|
        apdu->a_secure_messaging = apdu->a_cla & 0xe;
 | 
						|
        break;
 | 
						|
    case 0xb0:
 | 
						|
    case 0xc0:
 | 
						|
        break;
 | 
						|
 | 
						|
    case 0x10:
 | 
						|
    case 0x20:
 | 
						|
    case 0x30:
 | 
						|
    case 0x40:
 | 
						|
    case 0x50:
 | 
						|
    case 0x60:
 | 
						|
    case 0x70:
 | 
						|
        /* Reserved for future use */
 | 
						|
        apdu->a_gen_type = VCARD_7816_RFU;
 | 
						|
        break;
 | 
						|
    case 0xd0:
 | 
						|
    case 0xe0:
 | 
						|
    case 0xf0:
 | 
						|
    default:
 | 
						|
        apdu->a_gen_type =
 | 
						|
            (apdu->a_cla == 0xff) ? VCARD_7816_PTS : VCARD_7816_PROPRIETARY;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    return VCARD7816_STATUS_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * set the Le and Lc fields according to table 5 of the
 | 
						|
 * 7816-4 part 4 spec
 | 
						|
 */
 | 
						|
static vcard_7816_status_t
 | 
						|
vcard_apdu_set_length(VCardAPDU *apdu)
 | 
						|
{
 | 
						|
    int L, Le;
 | 
						|
 | 
						|
    /* process according to table 5 of the 7816-4 Part 4 spec.
 | 
						|
     * variable names match the variables in the spec */
 | 
						|
    L = apdu->a_len-4; /* fixed APDU header */
 | 
						|
    apdu->a_Lc = 0;
 | 
						|
    apdu->a_Le = 0;
 | 
						|
    apdu->a_body = NULL;
 | 
						|
    switch (L) {
 | 
						|
    case 0:
 | 
						|
        /* 1 minimal apdu */
 | 
						|
        return VCARD7816_STATUS_SUCCESS;
 | 
						|
    case 1:
 | 
						|
        /* 2S only return values apdu */
 | 
						|
        /*   zero maps to 256 here */
 | 
						|
        apdu->a_Le = apdu->a_header->ah_Le ?
 | 
						|
                         apdu->a_header->ah_Le : 256;
 | 
						|
        return VCARD7816_STATUS_SUCCESS;
 | 
						|
    default:
 | 
						|
        /* if the ah_Le byte is zero and we have more than
 | 
						|
         * 1 byte in the header, then we must be using extended Le and Lc.
 | 
						|
         * process the extended now. */
 | 
						|
        if (apdu->a_header->ah_Le == 0) {
 | 
						|
            if (L < 3) {
 | 
						|
                /* coding error, need at least 3 bytes */
 | 
						|
                return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
 | 
						|
            }
 | 
						|
            /* calculate the first extended value. Could be either Le or Lc */
 | 
						|
            Le = (apdu->a_header->ah_body[0] << 8)
 | 
						|
               || apdu->a_header->ah_body[1];
 | 
						|
            if (L == 3) {
 | 
						|
                /* 2E extended, return data only */
 | 
						|
                /*   zero maps to 65536 */
 | 
						|
                apdu->a_Le = Le ? Le : 65536;
 | 
						|
                return VCARD7816_STATUS_SUCCESS;
 | 
						|
            }
 | 
						|
            if (Le == 0) {
 | 
						|
                /* reserved for future use, probably for next time we need
 | 
						|
                 * to extend the lengths */
 | 
						|
                return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
 | 
						|
            }
 | 
						|
            /* we know that the first extended value is Lc now */
 | 
						|
            apdu->a_Lc = Le;
 | 
						|
            apdu->a_body = &apdu->a_header->ah_body[2];
 | 
						|
            if (L == Le+3) {
 | 
						|
                /* 3E extended, only body parameters */
 | 
						|
                return VCARD7816_STATUS_SUCCESS;
 | 
						|
            }
 | 
						|
            if (L == Le+5) {
 | 
						|
                /* 4E extended, parameters and return data */
 | 
						|
                Le = (apdu->a_data[apdu->a_len-2] << 8)
 | 
						|
                   || apdu->a_data[apdu->a_len-1];
 | 
						|
                apdu->a_Le = Le ? Le : 65536;
 | 
						|
                return VCARD7816_STATUS_SUCCESS;
 | 
						|
            }
 | 
						|
            return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
 | 
						|
        }
 | 
						|
        /* not extended */
 | 
						|
        apdu->a_Lc = apdu->a_header->ah_Le;
 | 
						|
        apdu->a_body = &apdu->a_header->ah_body[0];
 | 
						|
        if (L ==  apdu->a_Lc + 1) {
 | 
						|
            /* 3S only body parameters */
 | 
						|
            return VCARD7816_STATUS_SUCCESS;
 | 
						|
        }
 | 
						|
        if (L ==  apdu->a_Lc + 2) {
 | 
						|
            /* 4S parameters and return data */
 | 
						|
            Le = apdu->a_data[apdu->a_len-1];
 | 
						|
            apdu->a_Le = Le ?  Le : 256;
 | 
						|
            return VCARD7816_STATUS_SUCCESS;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * create a new APDU from a raw set of bytes. This will decode all the
 | 
						|
 * above fields. users of VCARDAPDU's can then depend on the already decoded
 | 
						|
 * values.
 | 
						|
 */
 | 
						|
VCardAPDU *
 | 
						|
vcard_apdu_new(unsigned char *raw_apdu, int len, vcard_7816_status_t *status)
 | 
						|
{
 | 
						|
    VCardAPDU *new_apdu;
 | 
						|
 | 
						|
    *status = VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
 | 
						|
    if (len < 4) {
 | 
						|
        *status = VCARD7816_STATUS_ERROR_WRONG_LENGTH;
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    new_apdu = g_new(VCardAPDU, 1);
 | 
						|
    new_apdu->a_data = g_memdup(raw_apdu, len);
 | 
						|
    new_apdu->a_len = len;
 | 
						|
    *status = vcard_apdu_set_class(new_apdu);
 | 
						|
    if (*status != VCARD7816_STATUS_SUCCESS) {
 | 
						|
        g_free(new_apdu);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    *status = vcard_apdu_set_length(new_apdu);
 | 
						|
    if (*status != VCARD7816_STATUS_SUCCESS) {
 | 
						|
        g_free(new_apdu);
 | 
						|
        new_apdu = NULL;
 | 
						|
    }
 | 
						|
    return new_apdu;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
vcard_apdu_delete(VCardAPDU *apdu)
 | 
						|
{
 | 
						|
    if (apdu == NULL) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    g_free(apdu->a_data);
 | 
						|
    g_free(apdu);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * declare response buffers for all the 7816 defined error codes
 | 
						|
 */
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_SUCCESS)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_RET_CORUPT)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_CHANGE)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FILE_FILLED)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_CHANGE)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_LENGTH)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(
 | 
						|
                    VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_INVALID)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NO_EF)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(
 | 
						|
                            VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FILE_NOT_FOUND)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_INS_CODE_INVALID)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_INVALID)
 | 
						|
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_GENERAL)
 | 
						|
 | 
						|
/*
 | 
						|
 * return a single response code. This function cannot fail. It will always
 | 
						|
 * return a response.
 | 
						|
 */
 | 
						|
VCardResponse *
 | 
						|
vcard_make_response(vcard_7816_status_t status)
 | 
						|
{
 | 
						|
    VCardResponse *response;
 | 
						|
 | 
						|
    switch (status) {
 | 
						|
    /* known 7816 response codes */
 | 
						|
    case VCARD7816_STATUS_SUCCESS:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_SUCCESS);
 | 
						|
    case VCARD7816_STATUS_WARNING:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_WARNING);
 | 
						|
    case VCARD7816_STATUS_WARNING_RET_CORUPT:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_WARNING_RET_CORUPT);
 | 
						|
    case VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE);
 | 
						|
    case VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED);
 | 
						|
    case VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID);
 | 
						|
    case VCARD7816_STATUS_WARNING_CHANGE:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_WARNING_CHANGE);
 | 
						|
    case VCARD7816_STATUS_WARNING_FILE_FILLED:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_WARNING_FILE_FILLED);
 | 
						|
    case VCARD7816_STATUS_EXC_ERROR:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_EXC_ERROR);
 | 
						|
    case VCARD7816_STATUS_EXC_ERROR_CHANGE:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_EXC_ERROR_CHANGE);
 | 
						|
    case VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
 | 
						|
    case VCARD7816_STATUS_ERROR_WRONG_LENGTH:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_WRONG_LENGTH);
 | 
						|
    case VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED);
 | 
						|
    case VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED);
 | 
						|
    case VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
 | 
						|
    case VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
 | 
						|
    case VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE);
 | 
						|
    case VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED);
 | 
						|
    case VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED);
 | 
						|
    case VCARD7816_STATUS_ERROR_DATA_INVALID:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_DATA_INVALID);
 | 
						|
    case VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
 | 
						|
    case VCARD7816_STATUS_ERROR_DATA_NO_EF:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_DATA_NO_EF);
 | 
						|
    case VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING);
 | 
						|
    case VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT);
 | 
						|
    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
 | 
						|
    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA);
 | 
						|
    case VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
 | 
						|
    case VCARD7816_STATUS_ERROR_FILE_NOT_FOUND:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
 | 
						|
    case VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND);
 | 
						|
    case VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE);
 | 
						|
    case VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT);
 | 
						|
    case VCARD7816_STATUS_ERROR_P1_P2_INCORRECT:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
 | 
						|
    case VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT);
 | 
						|
    case VCARD7816_STATUS_ERROR_DATA_NOT_FOUND:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
 | 
						|
    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2);
 | 
						|
    case VCARD7816_STATUS_ERROR_INS_CODE_INVALID:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_INS_CODE_INVALID);
 | 
						|
    case VCARD7816_STATUS_ERROR_CLA_INVALID:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_CLA_INVALID);
 | 
						|
    case VCARD7816_STATUS_ERROR_GENERAL:
 | 
						|
        return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                    VCARD7816_STATUS_ERROR_GENERAL);
 | 
						|
    default:
 | 
						|
        /* we don't know this status code, create a response buffer to
 | 
						|
         * hold it */
 | 
						|
        response = vcard_response_new_status(status);
 | 
						|
        if (response == NULL) {
 | 
						|
            /* couldn't allocate the buffer, return memmory error */
 | 
						|
            return VCARD_RESPONSE_GET_STATIC(
 | 
						|
                        VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
 | 
						|
        }
 | 
						|
        return response;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Add File card support here if you need it.
 | 
						|
 */
 | 
						|
static VCardStatus
 | 
						|
vcard7816_file_system_process_apdu(VCard *card, VCardAPDU *apdu,
 | 
						|
                                   VCardResponse **response)
 | 
						|
{
 | 
						|
    /* TODO: if we want to support a virtual file system card, we do it here.
 | 
						|
     * It would probably be a pkcs #15 card type */
 | 
						|
    *response = vcard_make_response(
 | 
						|
                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
 | 
						|
    return VCARD_DONE;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * VM card (including java cards)
 | 
						|
 */
 | 
						|
static VCardStatus
 | 
						|
vcard7816_vm_process_apdu(VCard *card, VCardAPDU *apdu,
 | 
						|
                          VCardResponse **response)
 | 
						|
{
 | 
						|
    int bytes_to_copy, next_byte_count, count;
 | 
						|
    VCardApplet *current_applet;
 | 
						|
    VCardBufferResponse *buffer_response;
 | 
						|
    vcard_7816_status_t status;
 | 
						|
 | 
						|
    /* parse the class first */
 | 
						|
    if (apdu->a_gen_type !=  VCARD_7816_ISO) {
 | 
						|
        *response = vcard_make_response(
 | 
						|
                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
 | 
						|
        return VCARD_DONE;
 | 
						|
    }
 | 
						|
 | 
						|
    /* use a switch so that if we need to support secure channel stuff later,
 | 
						|
     * we know where to put it */
 | 
						|
    switch (apdu->a_secure_messaging) {
 | 
						|
    case 0x0: /* no SM */
 | 
						|
        break;
 | 
						|
    case 0x4: /* proprietary SM */
 | 
						|
    case 0x8: /* header not authenticated */
 | 
						|
    case 0xc: /* header authenticated */
 | 
						|
    default:
 | 
						|
        /* for now, don't try to support secure channel stuff in the
 | 
						|
         * virtual card. */
 | 
						|
        *response = vcard_make_response(
 | 
						|
                        VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
 | 
						|
        return VCARD_DONE;
 | 
						|
    }
 | 
						|
 | 
						|
    /* now parse the instruction */
 | 
						|
    switch (apdu->a_ins) {
 | 
						|
    case  VCARD7816_INS_MANAGE_CHANNEL: /* secure channel op */
 | 
						|
    case  VCARD7816_INS_EXTERNAL_AUTHENTICATE: /* secure channel op */
 | 
						|
    case  VCARD7816_INS_GET_CHALLENGE: /* secure channel op */
 | 
						|
    case  VCARD7816_INS_INTERNAL_AUTHENTICATE: /* secure channel op */
 | 
						|
    case  VCARD7816_INS_ERASE_BINARY: /* applet control op */
 | 
						|
    case  VCARD7816_INS_READ_BINARY: /* applet control op */
 | 
						|
    case  VCARD7816_INS_WRITE_BINARY: /* applet control op */
 | 
						|
    case  VCARD7816_INS_UPDATE_BINARY: /* applet control op */
 | 
						|
    case  VCARD7816_INS_READ_RECORD: /* file op */
 | 
						|
    case  VCARD7816_INS_WRITE_RECORD: /* file op */
 | 
						|
    case  VCARD7816_INS_UPDATE_RECORD: /* file op */
 | 
						|
    case  VCARD7816_INS_APPEND_RECORD: /* file op */
 | 
						|
    case  VCARD7816_INS_ENVELOPE:
 | 
						|
    case  VCARD7816_INS_PUT_DATA:
 | 
						|
        *response = vcard_make_response(
 | 
						|
                            VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
 | 
						|
        break;
 | 
						|
 | 
						|
    case  VCARD7816_INS_SELECT_FILE:
 | 
						|
        if (apdu->a_p1 != 0x04) {
 | 
						|
            *response = vcard_make_response(
 | 
						|
                            VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        /* side effect, deselect the current applet if no applet has been found
 | 
						|
         * */
 | 
						|
        current_applet = vcard_find_applet(card, apdu->a_body, apdu->a_Lc);
 | 
						|
        vcard_select_applet(card, apdu->a_channel, current_applet);
 | 
						|
        if (current_applet) {
 | 
						|
            unsigned char *aid;
 | 
						|
            int aid_len;
 | 
						|
            aid = vcard_applet_get_aid(current_applet, &aid_len);
 | 
						|
            *response = vcard_response_new(card, aid, aid_len, apdu->a_Le,
 | 
						|
                                          VCARD7816_STATUS_SUCCESS);
 | 
						|
        } else {
 | 
						|
            *response = vcard_make_response(
 | 
						|
                             VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
    case  VCARD7816_INS_VERIFY:
 | 
						|
        if ((apdu->a_p1 != 0x00) || (apdu->a_p2 != 0x00)) {
 | 
						|
            *response = vcard_make_response(
 | 
						|
                            VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
 | 
						|
        } else {
 | 
						|
            if (apdu->a_Lc == 0) {
 | 
						|
                /* handle pin count if possible */
 | 
						|
                count = vcard_emul_get_login_count(card);
 | 
						|
                if (count < 0) {
 | 
						|
                    *response = vcard_make_response(
 | 
						|
                                    VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
 | 
						|
                } else {
 | 
						|
                    if (count > 0xf) {
 | 
						|
                        count = 0xf;
 | 
						|
                    }
 | 
						|
                    *response = vcard_response_new_status_bytes(
 | 
						|
                                                VCARD7816_SW1_WARNING_CHANGE,
 | 
						|
                                                                0xc0 | count);
 | 
						|
                    if (*response == NULL) {
 | 
						|
                        *response = vcard_make_response(
 | 
						|
                                    VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                    status = vcard_emul_login(card, apdu->a_body, apdu->a_Lc);
 | 
						|
                *response = vcard_make_response(status);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
    case VCARD7816_INS_GET_RESPONSE:
 | 
						|
        buffer_response = vcard_get_buffer_response(card);
 | 
						|
        if (!buffer_response) {
 | 
						|
            *response = vcard_make_response(
 | 
						|
                            VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
 | 
						|
            /* handle error */
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        bytes_to_copy = MIN(buffer_response->len, apdu->a_Le);
 | 
						|
        next_byte_count = MIN(256, buffer_response->len - bytes_to_copy);
 | 
						|
        *response = vcard_response_new_bytes(
 | 
						|
                        card, buffer_response->current, bytes_to_copy,
 | 
						|
                        apdu->a_Le,
 | 
						|
                        next_byte_count ?
 | 
						|
                        VCARD7816_SW1_RESPONSE_BYTES : VCARD7816_SW1_SUCCESS,
 | 
						|
                        next_byte_count);
 | 
						|
        buffer_response->current += bytes_to_copy;
 | 
						|
        buffer_response->len -= bytes_to_copy;
 | 
						|
        if (*response == NULL || (next_byte_count == 0)) {
 | 
						|
            vcard_set_buffer_response(card, NULL);
 | 
						|
            vcard_buffer_response_delete(buffer_response);
 | 
						|
        }
 | 
						|
        if (*response == NULL) {
 | 
						|
            *response =
 | 
						|
                vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
    case VCARD7816_INS_GET_DATA:
 | 
						|
        *response =
 | 
						|
            vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
 | 
						|
        break;
 | 
						|
 | 
						|
    default:
 | 
						|
        *response =
 | 
						|
            vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    /* response should have been set somewhere */
 | 
						|
    assert(*response != NULL);
 | 
						|
    return VCARD_DONE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * APDU processing starts here. This routes the card processing stuff to the
 | 
						|
 * right location.
 | 
						|
 */
 | 
						|
VCardStatus
 | 
						|
vcard_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
 | 
						|
{
 | 
						|
    VCardStatus status;
 | 
						|
    VCardBufferResponse *buffer_response;
 | 
						|
 | 
						|
    /* first handle any PTS commands, which aren't really APDU's */
 | 
						|
    if (apdu->a_type == VCARD_7816_PTS) {
 | 
						|
        /* the PTS responses aren't really responses either */
 | 
						|
        *response = vcard_response_new_data(apdu->a_data, apdu->a_len);
 | 
						|
        /* PTS responses have no status bytes */
 | 
						|
        (*response)->b_total_len = (*response)->b_len;
 | 
						|
        return VCARD_DONE;
 | 
						|
    }
 | 
						|
    buffer_response = vcard_get_buffer_response(card);
 | 
						|
    if (buffer_response && apdu->a_ins != VCARD7816_INS_GET_RESPONSE) {
 | 
						|
        /* clear out buffer_response, return an error */
 | 
						|
        vcard_set_buffer_response(card, NULL);
 | 
						|
        vcard_buffer_response_delete(buffer_response);
 | 
						|
        *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR);
 | 
						|
        return VCARD_DONE;
 | 
						|
    }
 | 
						|
 | 
						|
    status = vcard_process_applet_apdu(card, apdu, response);
 | 
						|
    if (status != VCARD_NEXT) {
 | 
						|
        return status;
 | 
						|
    }
 | 
						|
    switch (vcard_get_type(card)) {
 | 
						|
    case VCARD_FILE_SYSTEM:
 | 
						|
        return vcard7816_file_system_process_apdu(card, apdu, response);
 | 
						|
    case VCARD_VM:
 | 
						|
        return vcard7816_vm_process_apdu(card, apdu, response);
 | 
						|
    case VCARD_DIRECT:
 | 
						|
        /* if we are type direct, then the applet should handle everything */
 | 
						|
        assert(!"VCARD_DIRECT: applet failure");
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    *response =
 | 
						|
        vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
 | 
						|
    return VCARD_DONE;
 | 
						|
}
 |